recharts#Bar TypeScript Examples

The following examples show how to use recharts#Bar. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: WeeklyImpactResponder.tsx    From backstage-plugin-opsgenie with MIT License 6 votes vote down vote up
WeeklyImpactResponders = ({ context }: { context: Context }) => {
    const graphId = "weekly-impact-responders";
    const analyticsApi = useApi(analyticsApiRef);
    const data = analyticsApi.impactByWeekAndResponder(context);

    return (
        <InfoCard title="Average impact by week and responder" action={<SaveAction targetRef={graphId} />}>
            <div id={graphId} style={{ width: '100%', height: 450, paddingTop: '1.2rem', paddingRight: '1.2rem' }}>
                <ResponsiveContainer>
                    <ComposedChart data={data.dataPoints}>
                        <CartesianGrid strokeDasharray="3 3" />

                        <XAxis dataKey="period" />
                        <YAxis tickFormatter={value => value === 0 ? '0 min' : moment.duration(value, 'minutes').humanize()} />

                        {data.responders.map(responder => (
                            <Bar dataKey={responder} fill={colorForString(responder)} stackId="a" barSize={30} key={responder} />
                        ))}

                        <Tooltip
                            formatter={(value: number, name: string) => [value === 0 ? '0 min' : moment.duration(value, 'minutes').humanize(), name]}
                            content={<FilterZeroTooltip />}
                        />
                        <Legend />
                    </ComposedChart>
                </ResponsiveContainer>
            </div>
        </InfoCard>
    );
}
Example #2
Source File: WeeklyIncidents.tsx    From backstage-plugin-opsgenie with MIT License 6 votes vote down vote up
Graph = ({context}: {context: Context}) => {
    const analyticsApi = useApi(analyticsApiRef);
    const dataPoints = analyticsApi.incidentsByWeekAndHours(context);

    return (
        <div id="weekly-incidents" style={{ width: '100%', height: 300, paddingTop: '1.2rem', paddingRight: '1.2rem' }}>
            <ResponsiveContainer>
                <ComposedChart
                    data={dataPoints}
                >
                    <CartesianGrid strokeDasharray="3 3" />
                    <XAxis dataKey="week" />
                    <YAxis />
                    <Bar dataKey="businessHours" fill="#82ca9d" name="Business hours" stackId="a" barSize={30} />
                    <Bar dataKey="onCallHours" fill="#8884d8" name="On-call hours" stackId="a" barSize={30} />
                    <Line type="monotone" dataKey="total" name="Total" stroke="#ff7300" />
                    <Tooltip content={<FilterZeroTooltip />} />
                    <Legend />
                </ComposedChart>
            </ResponsiveContainer>
        </div>
    );
}
Example #3
Source File: WeeklyIncidentsSeverity.tsx    From backstage-plugin-opsgenie with MIT License 6 votes vote down vote up
Graph = ({context}: {context: Context}) => {
    const analyticsApi = useApi(analyticsApiRef);
    const dataPoints = analyticsApi.incidentsByWeekAndSeverity(context);

    return (
        <div id="weekly-incidents-severity" style={{ width: '100%', height: 300, paddingTop: '1.2rem', paddingRight: '1.2rem' }}>
            <ResponsiveContainer>
                <ComposedChart data={dataPoints}>
                    <CartesianGrid strokeDasharray="3 3" />
                    <XAxis dataKey="week" />
                    <YAxis />
                    <Bar dataKey="p1" fill="#bf2600" name="P1 - Critical" stackId="a" barSize={30} />
                    <Bar dataKey="p2" fill="#ff7452" name="P2 - High" stackId="a" barSize={30} />
                    <Bar dataKey="p3" fill="#ffab00" name="P3 - Moderate" stackId="a" barSize={30} />
                    <Bar dataKey="p4" fill="#36b37e" name="P4 - Low" stackId="a" barSize={30} />
                    <Bar dataKey="p5" fill="#00857A" name="P5 - Informational" stackId="a" barSize={30} />
                    <Tooltip content={<FilterZeroTooltip />} />
                    <Legend />
                </ComposedChart>
            </ResponsiveContainer>
        </div>
    );
}
Example #4
Source File: PeriodByResponderGraph.tsx    From backstage-plugin-opsgenie with MIT License 6 votes vote down vote up
PeriodByResponderGraph = ({data}: {data: IncidentsByResponders}) => {
    return (
        <ResponsiveContainer>
            <ComposedChart data={data.dataPoints}>
                <CartesianGrid strokeDasharray="3 3" />
                <XAxis dataKey="period" />
                <YAxis />
                {data.responders.map(responder => (
                    <Bar dataKey={responder} fill={colorForString(responder)} stackId="a" barSize={30}  key={responder} />
                ))}
                <Line type="monotone" dataKey="total" name="Total" stroke="#ff7300" />
                <Tooltip content={<FilterZeroTooltip />} />
                <Legend />
            </ComposedChart>
        </ResponsiveContainer>
    );
}
Example #5
Source File: ChartSpans.tsx    From kubenav with MIT License 6 votes vote down vote up
ChartSpans: React.FunctionComponent<IChartSpansProps> = ({ spans }: IChartSpansProps) => {
  const traceStartTime = spans[0].startTime;
  const data: IData[] = [];

  for (const span of spans) {
    const offset = span.startTime - traceStartTime;
    data.push({
      time: span.duration / 1000,
      offset: offset / 1000,
    });
  }

  return (
    <IonRow style={{ minHeight: `50px`, width: '100%' }}>
      <IonCol style={{ padding: '0px' }}>
        <ResponsiveContainer>
          <ComposedChart layout="vertical" data={data} barSize={2}>
            <XAxis type="number" hide={true} domain={['dataMin', 'dataMax']} />
            <Bar stackId="span" dataKey="offset" fill="#326ce5" fillOpacity={0} isAnimationActive={false} />
            <Bar stackId="span" dataKey="time" fill="#326ce5" isAnimationActive={false} />
          </ComposedChart>
        </ResponsiveContainer>
      </IonCol>
    </IonRow>
  );
}
Example #6
Source File: QueueSizeChart.tsx    From asynqmon with MIT License 6 votes vote down vote up
function QueueSizeChart(props: Props) {
  const theme = useTheme();
  const handleClick = (params: { activeLabel?: string } | null) => {
    const allQueues = props.data.map((b) => b.queue);
    if (
      params &&
      params.activeLabel &&
      allQueues.includes(params.activeLabel)
    ) {
      history.push(queueDetailsPath(params.activeLabel));
    }
  };
  const history = useHistory();
  return (
    <ResponsiveContainer>
      <BarChart
        data={props.data}
        maxBarSize={120}
        onClick={handleClick}
        style={{ cursor: "pointer" }}
      >
        <CartesianGrid strokeDasharray="3 3" />
        <XAxis dataKey="queue" stroke={theme.palette.text.secondary} />
        <YAxis stroke={theme.palette.text.secondary} />
        <Tooltip />
        <Legend />
        <Bar dataKey="active" stackId="a" fill="#1967d2" />
        <Bar dataKey="pending" stackId="a" fill="#669df6" />
        <Bar dataKey="aggregating" stackId="a" fill="#e69138" />
        <Bar dataKey="scheduled" stackId="a" fill="#fdd663" />
        <Bar dataKey="retry" stackId="a" fill="#f666a9" />
        <Bar dataKey="archived" stackId="a" fill="#ac4776" />
        <Bar dataKey="completed" stackId="a" fill="#4bb543" />
      </BarChart>
    </ResponsiveContainer>
  );
}
Example #7
Source File: ProcessedTasksChart.tsx    From asynqmon with MIT License 6 votes vote down vote up
function ProcessedTasksChart(props: Props) {
  const theme = useTheme<Theme>();
  return (
    <ResponsiveContainer>
      <BarChart data={props.data} maxBarSize={120}>
        <CartesianGrid strokeDasharray="3 3" />
        <XAxis dataKey="queue" stroke={theme.palette.text.secondary} />
        <YAxis stroke={theme.palette.text.secondary} />
        <Tooltip />
        <Legend />
        <Bar
          dataKey="succeeded"
          stackId="a"
          fill={theme.palette.success.light}
        />
        <Bar dataKey="failed" stackId="a" fill={theme.palette.error.light} />
      </BarChart>
    </ResponsiveContainer>
  );
}
Example #8
Source File: BuildTimeline.tsx    From backstage with Apache License 2.0 6 votes vote down vote up
BuildTimeline = ({
  targets,
  height,
  width,
}: BuildTimelineProps) => {
  const theme = useTheme();
  if (!targets.length) return <p>No Targets</p>;

  const data = getTimelineData(targets);

  return (
    <ResponsiveContainer
      height={height}
      width={width}
      minHeight={EMPTY_HEIGHT + targets.length * 5}
    >
      <BarChart layout="vertical" data={data} maxBarSize={10} barGap={0}>
        <CartesianGrid strokeDasharray="2 2" />
        <XAxis type="number" domain={[0, 'dataMax']} />
        <YAxis type="category" dataKey="name" padding={{ top: 0, bottom: 0 }} />
        <Tooltip content={<TargetToolTip />} />
        <Legend />
        <Bar
          dataKey="buildTime"
          fill={theme.palette.grey[400]}
          minPointSize={1}
        />
        <Bar dataKey="compileTime" fill={theme.palette.primary.main} />
      </BarChart>
    </ResponsiveContainer>
  );
}
Example #9
Source File: Ages.tsx    From covid19map with MIT License 6 votes vote down vote up
Ages = ({ ages }: any) => {
  const theme = useTheme();
  return (
    <StyledAges>
      <div className="head">Cases by Age</div>
      <div className="chart-wrap">
        <ResponsiveContainer width="100%" height="100%">
          <BarChart
            data={ages}
            layout="vertical"
            margin={{
              top: 10,
              right: 0,
              left: 0,
              bottom: 10,
            }}
            // @ts-ignore
            isAnimationActive={false}
          >
            <XAxis type="number" hide />
            <YAxis type="category" dataKey="group" interval={0} width={90} />

            <Bar dataKey="active" fill={theme.teal} stackId="a" />
            <Bar dataKey="recovered" fill={theme.green} stackId="a" />
            <Bar dataKey="deaths" fill={theme.navy} stackId="a" />
          </BarChart>
        </ResponsiveContainer>
      </div>
      <ChartLegend
        items={[
          { title: "Active", color: theme.teal },
          { title: "Recovered", color: theme.green },
          { title: "Deaths", color: theme.navy },
        ]}
      />
    </StyledAges>
  );
}
Example #10
Source File: BarChart.tsx    From opensaas with MIT License 6 votes vote down vote up
BarChart: React.FC<BarChartProps> = (props) => {
  const { width, height, data, barSize, barCategoryGap, showGrid, showLegend, colors } = props;
  return (
    <ResponsiveContainer>
      <RechartsBarChart
        width={width}
        height={height}
        data={data}
        barSize={barSize}
        barCategoryGap={barCategoryGap}
        margin={{
          top: 5,
          right: 30,
          left: 10,
          bottom: 5,
        }}>
        {showGrid ? <CartesianGrid strokeDasharray='3 3' /> : null}
        <XAxis dataKey='name' axisLine={false} tickLine={false} />
        <YAxis axisLine={false} tickLine={false} />
        <Tooltip cursor={false} />
        {showLegend ? <Legend /> : null}
        {Object.keys(data[0]).map((key: string, index: number) => {
          return key !== 'name' ? <Bar dataKey={key} key={key} fill={colors[(index - 1) % colors.length]} /> : null;
        })}
      </RechartsBarChart>
    </ResponsiveContainer>
  );
}
Example #11
Source File: RegionAgeGenderChart.tsx    From covid19map with MIT License 6 votes vote down vote up
RegionAgeGenderChart = ({ data }: { data: any }) => {
  const theme = useTheme();
  return (
    <StyledRegionAgeGenderChart>
      <h3>Age Groups by DHB</h3>
      <div className="chart-wrap">
        <ResponsiveContainer width="100%" height="100%">
          <BarChart
            data={data}
            layout="vertical"
            margin={{
              top: 10,
              right: 0,
              left: 0,
              bottom: 10,
            }}
            // @ts-ignore
            isAnimationActive={false}
          >
            <XAxis type="number" hide />
            <YAxis type="category" dataKey="age" interval={0} width={90} />
            <Bar dataKey="male" fill={theme.teal} stackId="a" />
            <Bar dataKey="female" fill={theme.green} stackId="a" />
          </BarChart>
        </ResponsiveContainer>
      </div>
      <div className="legend">
        <div className="legend-item male">Male</div>
        <div className="legend-item female">Female</div>
      </div>
    </StyledRegionAgeGenderChart>
  );
}
Example #12
Source File: index.tsx    From vvs-ui with GNU General Public License v3.0 5 votes vote down vote up
Chart = ({ data, setHoverValue, setHoverDate }: LineChartProps) => {
  const { theme } = useTheme()
  if (!data || data.length === 0) {
    return <BarChartLoader />
  }
  return (
    <ResponsiveContainer width="100%" height="100%">
      <BarChart
        data={data}
        margin={{
          top: 5,
          right: 15,
          left: 0,
          bottom: 5,
        }}
        onMouseLeave={() => {
          setHoverDate(undefined)
          setHoverValue(undefined)
        }}
      >
        <XAxis
          dataKey="time"
          axisLine={false}
          tickLine={false}
          tickFormatter={(time) => format(time, 'dd')}
          minTickGap={10}
        />
        <YAxis
          dataKey="value"
          tickCount={6}
          scale="linear"
          axisLine={false}
          tickLine={false}
          color={theme.colors.textSubtle}
          fontSize="12px"
          tickFormatter={(val) => `$${formatAmount(val)}`}
          orientation="right"
          tick={{ dx: 10, fill: theme.colors.textSubtle }}
        />
        <Tooltip
          cursor={{ fill: theme.colors.backgroundDisabled }}
          contentStyle={{ display: 'none' }}
          formatter={(tooltipValue, name, props) => (
            <HoverUpdater payload={props.payload} setHoverValue={setHoverValue} setHoverDate={setHoverDate} />
          )}
        />
        <Bar
          dataKey="value"
          fill={theme.colors.primary}
          shape={(props) => (
            <CustomBar height={props.height} width={props.width} x={props.x} y={props.y} fill={theme.colors.primary} />
          )}
        />
      </BarChart>
    </ResponsiveContainer>
  )
}
Example #13
Source File: DistributionBar.tsx    From mStable-apps with GNU Lesser General Public License v3.0 5 votes vote down vote up
DistributionBar: FC = () => {
  const [epochData] = useEpochData()

  const [, setSelectedDialId] = useSelectedDialId()
  const [, setHoveredDialId] = useHoveredDialId()

  const scaledDialVotes = useScaledDialVotes(epochData?.dialVotes)

  return (
    <Container>
      <Inner>
        <Header>
          <div>
            <h4>
              <span>Distribution</span>
            </h4>
          </div>
          <div>
            <CountUp end={epochData?.emission} decimals={0} duration={0.3} />
            <StyledTokenIcon symbol="MTA" />
          </div>
        </Header>
        <ResponsiveContainer height={24} width="100%">
          <BarChart layout="vertical" stackOffset="none" data={scaledDialVotes} margin={{ top: 0, bottom: 0, left: 0, right: 0 }}>
            <XAxis hide type="number" />
            <YAxis hide type="category" />
            {Object.values(epochData?.dialVotes ?? {})
              .filter(dialVote => dialVote.votes > 0)
              .map(({ dialId }, idx, arr) => (
                <Bar
                  key={dialId}
                  dataKey={dialId}
                  fill={DIALS_METADATA[dialId].color}
                  stackId="bar"
                  radius={renderRadius(idx, arr.length)}
                  onClick={() => {
                    setSelectedDialId(dialId)
                  }}
                  onMouseEnter={() => {
                    setHoveredDialId(dialId)
                  }}
                  onMouseLeave={() => {
                    setHoveredDialId(undefined)
                  }}
                />
              ))}
          </BarChart>
        </ResponsiveContainer>
        <ActiveDial />
      </Inner>
    </Container>
  )
}
Example #14
Source File: Chart.tsx    From kubenav with MIT License 5 votes vote down vote up
Chart: React.FunctionComponent<IChartProps> = ({ aggregations }: IChartProps) => {
  const context = useContext<IContext>(AppContext);

  const formatTime = (time: number): string => {
    const d = new Date(time);
    return `${('0' + (d.getMonth() + 1)).slice(-2)}/${('0' + d.getDate()).slice(-2)} ${('0' + d.getHours()).slice(
      -2,
    )}:${('0' + d.getMinutes()).slice(-2)}`;
  };

  return (
    <IonRow style={{ height: '200px', width: '100%' }}>
      <IonCol style={{ padding: '0px' }}>
        <ResponsiveContainer>
          <BarChart data={aggregations?.logcount?.buckets}>
            <XAxis
              dataKey="key"
              scale="time"
              type="number"
              domain={['dataMin', 'dataMax']}
              tickFormatter={formatTime}
            />
            {!isPlatform('hybrid') ? (
              <Tooltip
                cursor={{ stroke: '#949494', strokeWidth: 2 }}
                contentStyle={
                  isDarkMode(context.settings.theme)
                    ? isPlatform('ios')
                      ? { backgroundColor: '1c1c1c', borderColor: '#949494' }
                      : { backgroundColor: '#1A1B1E', borderColor: '#949494' }
                    : { backgroundColor: '#ffffff', borderColor: '#949494' }
                }
                formatter={(value) => {
                  return [value, 'Count'];
                }}
                labelFormatter={formatTime}
              />
            ) : null}
            <Bar dataKey="doc_count" stroke="#326ce5" fill="#326ce5" />
          </BarChart>
        </ResponsiveContainer>
      </IonCol>
    </IonRow>
  );
}
Example #15
Source File: index.tsx    From glide-frontend with GNU General Public License v3.0 5 votes vote down vote up
Chart = ({ data, setHoverValue, setHoverDate }: LineChartProps) => {
  const { theme } = useTheme()
  if (!data || data.length === 0) {
    return <BarChartLoader />
  }
  return (
    <ResponsiveContainer width="100%" height="100%">
      <BarChart
        data={data}
        margin={{
          top: 5,
          right: 15,
          left: 0,
          bottom: 5,
        }}
        onMouseLeave={() => {
          setHoverDate(undefined)
          setHoverValue(undefined)
        }}
      >
        <XAxis
          dataKey="time"
          axisLine={false}
          tickLine={false}
          tickFormatter={(time) => format(time, 'dd')}
          minTickGap={10}
        />
        <YAxis
          dataKey="value"
          tickCount={6}
          scale="linear"
          axisLine={false}
          tickLine={false}
          color={theme.colors.textSubtle}
          fontSize="12px"
          tickFormatter={(val) => `$${formatAmount(val)}`}
          orientation="right"
          tick={{ dx: 10, fill: theme.colors.textSubtle }}
        />
        <Tooltip
          cursor={{ fill: theme.colors.backgroundDisabled }}
          contentStyle={{ display: 'none' }}
          formatter={(tooltipValue, name, props) => (
            <HoverUpdater payload={props.payload} setHoverValue={setHoverValue} setHoverDate={setHoverDate} />
          )}
        />
        <Bar
          dataKey="value"
          fill={theme.colors.primary}
          shape={(props) => (
            <CustomBar height={props.height} width={props.width} x={props.x} y={props.y} fill={theme.colors.primary} />
          )}
        />
      </BarChart>
    </ResponsiveContainer>
  )
}
Example #16
Source File: InternationalBarChart.tsx    From covid19map with MIT License 5 votes vote down vote up
Ages = ({ data }: { data: any[] }) => {
  const theme = useTheme();
  const countries: any = {
    NZL: { name: "NZ", color: theme.teal },
    AUS: { name: "AU", color: theme.green },
    USA: { name: "USA", color: theme.navy },
    CHN: { name: "CHINA", color: "#317c3f" },
    ITA: { name: "ITALY", color: "#956828" },
    GBR: { name: "UK", color: "#d4b074" },
    KOR: { name: "S.KOREA", color: theme.yellow },
  };
  const dataWithNames = data.map((item: any) => {
    return {
      ...item,
      name: countries[item.country].name,
      country: countries[item.country],
    };
  });

  dataWithNames.sort((x, y) => (x.per1m > y.per1m ? -1 : 1));

  return (
    <StyledAges>
      <div className="head">Cases per 1 million</div>
      <div className="chart-wrap">
        <ResponsiveContainer width="100%" height="100%">
          <BarChart
            data={dataWithNames}
            layout="vertical"
            margin={{
              top: 10,
              right: 60,
              left: 0,
              bottom: 10,
            }}
            // @ts-ignore
            isAnimationActive={false}
          >
            <XAxis type="number" hide />
            <YAxis type="category" dataKey="name" interval={0} width={90} />
            <Bar
              dataKey="per1m"
              fill="#8884d8"
              label={{ position: "right", fill: theme.dark }}
              minPointSize={2}
            >
              {dataWithNames.map((entry, index) => (
                <Cell key={`cell-${index}`} fill={entry.country.color} />
              ))}
            </Bar>
          </BarChart>
        </ResponsiveContainer>
      </div>
    </StyledAges>
  );
}
Example #17
Source File: Question.tsx    From project-loved-web with MIT License 5 votes vote down vote up
export default function Question({
  answers,
  average,
  comparingStatistic,
  question,
  type,
}: ComparingQuestion | NormalQuestion) {
  const intl = useIntl();
  const colors = useColors();

  return (
    <div className='survey-question'>
      <div className='survey-question-info'>
        <h2>{question}</h2>
        {average != null && (
          <span
            style={{
              color: interpolateColor([
                colors['rating--2'],
                colors['rating--1'],
                colors['rating-0'],
                colors['rating-1'],
                colors['rating-2'],
              ])((average - 1) / 4),
            }}
          >
            {intl.formatNumber(average)}
          </span>
        )}
      </div>
      {type === '1to5' && comparingStatistic != null ? (
        <ComparingChart answers={answers} comparingStatistic={comparingStatistic} />
      ) : (
        <BarChart data={answers} width={700} height={175}>
          <XAxis
            dataKey='name'
            stroke={colors.content}
            tick={<XAxisTick />}
            tickLine={{ stroke: colors.content }}
          />
          <Bar dataKey='count' fill={colors.accent} label={{ fill: colors.background }} />
        </BarChart>
      )}
    </div>
  );
}
Example #18
Source File: InDepth.tsx    From backstage with Apache License 2.0 4 votes vote down vote up
export function InDepth() {
  const { releaseStats } = useReleaseStatsContext();
  const { averageReleaseTime, progress, releaseCommitPairs, run } =
    useGetReleaseTimes();

  const skipped =
    Object.keys(releaseStats.releases).length - releaseCommitPairs.length;

  return (
    <Box style={{ flex: 1 }}>
      <Box margin={1}>
        <Typography variant="h4">In-depth</Typography>
      </Box>

      <Box style={{ display: 'flex' }}>
        <Box margin={1} style={{ display: 'flex', flex: 1 }}>
          <Box>
            <Typography variant="h6">Release time</Typography>

            <Typography variant="body2">
              <strong>Release time</strong> is derived by comparing{' '}
              <i>createdAt</i> of the commits belonging to the first and last
              tag of each release. Releases without patches will have tags
              pointing towards the same commit and will thus be omitted. This
              project will omit {skipped} out of the total{' '}
              {Object.keys(releaseStats.releases).length} releases.
            </Typography>
          </Box>
        </Box>

        <Box
          margin={1}
          style={{ display: 'flex', flex: 1, flexDirection: 'column' }}
        >
          <Box>
            <Typography variant="h6">In numbers</Typography>

            <Typography variant="body2" color="textSecondary">
              <strong>Average release time</strong>:{' '}
              <AverageReleaseTime averageReleaseTime={averageReleaseTime} />
            </Typography>

            <Typography variant="body2" color="textSecondary">
              <strong>Lengthiest release</strong>:{' '}
              <LongestReleaseTime averageReleaseTime={averageReleaseTime} />
            </Typography>
          </Box>

          <Box marginTop={1}>
            {progress === 0 && (
              <MaterialTooltip
                title={`This action will send ~${
                  releaseCommitPairs.length * 2
                } requests`}
              >
                <Button
                  variant="contained"
                  color="secondary"
                  onClick={() => run()}
                  size="small"
                >
                  Crunch the numbers
                </Button>
              </MaterialTooltip>
            )}
          </Box>
        </Box>
      </Box>

      <Box marginTop={4}>
        <BarChart
          width={700}
          height={70 + averageReleaseTime.length * 22}
          data={
            averageReleaseTime.length > 0
              ? averageReleaseTime
              : [{ version: 'x.y.z', days: 0 }]
          }
          margin={{ top: 5, right: 30, left: 20, bottom: 5 }}
          layout="vertical"
        >
          <XAxis type="number" />
          <YAxis dataKey="version" type="category" />
          <Tooltip labelStyle={{ color: '#000', fontWeight: 'bold' }} />
          <Legend />
          <Bar dataKey="days" fill="#82ca9d" />
        </BarChart>

        {progress > 0 && progress < 100 && (
          <Box marginTop={1}>
            <LinearProgressWithLabel progress={progress} responseSteps={[]} />
          </Box>
        )}
      </Box>
    </Box>
  );
}
Example #19
Source File: BarChart.tsx    From backstage with Apache License 2.0 4 votes vote down vote up
BarChart = ({
  resources,
  responsive = true,
  displayAmount = 6,
  options = {},
  tooltip = defaultTooltip,
  onClick,
  onMouseMove,
}: BarChartProps) => {
  const theme = useTheme<CostInsightsTheme>();
  const styles = useBarChartStyles(theme);
  const [activeChart, setActiveChart] = useState(false);
  const [stepWindow, setStepWindow] = useState(() => [0, displayAmount]);

  const data = Object.assign(
    {
      previousFill: theme.palette.lightBlue,
      currentFill: theme.palette.darkBlue,
      previousName: 'Previous',
      currentName: 'Current',
    },
    options,
  );

  const [stepStart, stepEnd] = stepWindow;
  const steps = Math.ceil(resources.length / displayAmount);
  const disableStepper = resources.length <= displayAmount;

  const sortedResources = resources
    .sort(resourceSort)
    .slice(stepStart, stepEnd);

  // Pin the domain to the largest value in the series.
  // Intentionally redundant - This could simply be derived from the first element in the already sorted list,
  // but that may not be the case in the future when custom sorting is implemented.
  const globalResourcesMax = resources.reduce(
    (max, r: ResourceData) => Math.max(max, r.current, r.previous),
    0,
  );

  const onStepChange = useCallback(
    (activeStep: number) => {
      const start = activeStep * displayAmount;
      const end = start + displayAmount;
      if (end > resources.length) {
        setStepWindow([start, resources.length]);
      } else {
        setStepWindow([start, end]);
      }
    },
    [setStepWindow, resources, displayAmount],
  );

  return (
    <Box
      position="relative"
      onMouseLeave={() => setActiveChart(false)}
      onMouseEnter={() => setActiveChart(true)}
      data-testid="bar-chart-wrapper"
    >
      {/* Setting fixed values for height and width generates a console warning in testing but enables ResponsiveContainer to render its children. */}
      <ResponsiveContainer
        height={styles.container.height}
        width={responsive ? '100%' : styles.container.width}
      >
        <RechartsBarChart
          style={{ cursor: onClick ? 'pointer' : null }}
          onClick={onClick}
          onMouseMove={onMouseMove}
          data={sortedResources}
          margin={styles.barChart.margin}
          barSize={45}
          data-testid="bar-chart"
        >
          {tooltip && (
            <RechartsTooltip
              filterNull
              cursor={styles.cursor}
              animationDuration={100}
              content={tooltip}
            />
          )}
          <CartesianGrid
            vertical={false}
            stroke={styles.cartesianGrid.stroke}
          />
          <XAxis
            dataKey={DataKey.Name}
            tickLine={false}
            interval={0}
            height={styles.xAxis.height}
            tick={BarChartTick}
          />
          <YAxis
            tickFormatter={currencyFormatter.format}
            domain={[() => 0, globalResourcesMax]}
            tick={styles.axis}
          />
          <Bar
            dataKey={DataKey.Previous}
            name={data.previousName}
            fill={data.previousFill}
            isAnimationActive={false}
          />
          <Bar
            dataKey={DataKey.Current}
            name={data.currentName}
            fill={data.currentFill}
            isAnimationActive={false}
          />
        </RechartsBarChart>
      </ResponsiveContainer>
      {!disableStepper && (
        <BarChartStepper
          steps={steps}
          disableScroll={!activeChart}
          onChange={onStepChange}
        />
      )}
    </Box>
  );
}
Example #20
Source File: status-chart.tsx    From backstage with Apache License 2.0 4 votes vote down vote up
export function StatusChart(props: StatusChartProps) {
  const { analysis } = props;

  const { zoomFilterValues } = useZoom();
  const { zoomProps, getZoomArea } = useZoomArea();

  const values = useMemo(() => {
    return analysis.daily.values.map(value => {
      const totTriggers = analysis.daily.triggerReasons.reduce(
        (prev, cur) => prev + (value[cur as TriggerReason] ?? 0),
        0,
      );

      if (!totTriggers) {
        return value;
      }

      return {
        ...value,
        ...Object.fromEntries(
          analysis.daily.triggerReasons.map(reason => [
            reason,
            (value[reason as TriggerReason] ?? 0) / totTriggers,
          ]),
        ),
      };
    });
  }, [analysis.daily]);

  const triggerReasonLegendPayload = useMemo(
    (): NonNullable<LegendProps['payload']> =>
      analysis.daily.triggerReasons.map(reason => ({
        value: humanTriggerReason(reason),
        type: 'line',
        id: reason,
        color: triggerColorMap[reason as TriggerReason] ?? '',
      })),
    [analysis.daily.triggerReasons],
  );

  const statusesLegendPayload = useMemo(
    (): NonNullable<LegendProps['payload']> =>
      analysis.daily.statuses.map(status => ({
        value: capitalize(status),
        type: 'line',
        id: status,
        color: statusColorMap[status as FilterStatusType] ?? '',
      })),
    [analysis.daily.statuses],
  );

  const legendPayload = useMemo(
    (): NonNullable<LegendProps['payload']> => [
      ...triggerReasonLegendPayload,
      ...statusesLegendPayload,
    ],
    [statusesLegendPayload, triggerReasonLegendPayload],
  );

  const tooltipFormatter = useMemo(() => {
    const reasonSet = new Set(analysis.daily.triggerReasons);

    return (percentOrCount: number, name: string) => {
      const label = reasonSet.has(name)
        ? humanTriggerReason(name)
        : capitalize(name);
      const valueText = reasonSet.has(name)
        ? `${(percentOrCount * 100).toFixed(0)}%`
        : percentOrCount;

      return [
        <span>
          {label}: {valueText}
        </span>,
        null,
      ];
    };
  }, [analysis.daily.triggerReasons]);

  const zoomFilteredValues = useMemo(
    () => zoomFilterValues(values),
    [values, zoomFilterValues],
  );

  const barSize = getBarSize(analysis.daily.values.length);

  return (
    <Accordion defaultExpanded={analysis.daily.statuses.length > 1}>
      <AccordionSummary expandIcon={<ExpandMoreIcon />}>
        <Typography>
          Build count per status over build trigger reason
        </Typography>
      </AccordionSummary>
      <AccordionDetails>
        {values.length === 0 ? (
          <Alert severity="info">No data</Alert>
        ) : (
          <ResponsiveContainer width="100%" height={140}>
            <ComposedChart data={zoomFilteredValues} {...zoomProps}>
              <Legend payload={legendPayload} />
              <CartesianGrid strokeDasharray="3 3" />
              <XAxis
                dataKey="__epoch"
                type="category"
                tickFormatter={tickFormatterX}
              />
              <YAxis yAxisId={1} type="number" tickCount={5} name="Count" />
              <YAxis yAxisId={2} type="number" name="Triggers" hide />
              <Tooltip
                labelFormatter={labelFormatterWithoutTime}
                formatter={tooltipFormatter}
              />
              {triggerReasonLegendPayload.map(reason => (
                <Fragment key={reason.id}>
                  <Area
                    isAnimationActive={false}
                    type="monotone"
                    dataKey={reason.id!}
                    stackId="triggers"
                    yAxisId={2}
                    stroke={triggerColorMap[reason.id as TriggerReason] ?? ''}
                    fillOpacity={0.5}
                    fill={triggerColorMap[reason.id as TriggerReason] ?? ''}
                  />
                </Fragment>
              ))}
              {[...analysis.daily.statuses].reverse().map(status => (
                <Fragment key={status}>
                  <Bar
                    isAnimationActive={false}
                    type="monotone"
                    barSize={barSize}
                    dataKey={status}
                    stackId="statuses"
                    yAxisId={1}
                    stroke={statusColorMap[status as FilterStatusType] ?? ''}
                    fillOpacity={0.8}
                    fill={statusColorMap[status as FilterStatusType] ?? ''}
                  />
                </Fragment>
              ))}
              {getZoomArea({ yAxisId: 1 })}
            </ComposedChart>
          </ResponsiveContainer>
        )}
      </AccordionDetails>
    </Accordion>
  );
}
Example #21
Source File: stage-chart.tsx    From backstage with Apache License 2.0 4 votes vote down vote up
export function StageChart(props: StageChartProps) {
  const { stage, ...chartOptions } = props;
  const {
    chartTypes,
    defaultCollapsed = 0,
    defaultHidden = 0,
    zeroYAxis = false,
  } = chartOptions;

  const { zoomFilterValues } = useZoom();
  const { zoomProps, getZoomArea } = useZoomArea();

  const ticks = useMemo(
    () => pickElements(stage.values, 8).map(val => val.__epoch),
    [stage.values],
  );
  const domainY = useMemo(
    () => [zeroYAxis ? 0 : 'auto', 'auto'] as YAxisProps['domain'],
    [zeroYAxis],
  );
  const statuses = useMemo(
    () => statusTypes.filter(status => stage.statusSet.has(status)),
    [stage.statusSet],
  );
  const legendPayload = useMemo(
    (): LegendProps['payload'] =>
      statuses.map(status => ({
        value: capitalize(status),
        type: 'line',
        id: status,
        color: statusColorMap[status],
      })),
    [statuses],
  );

  const subStages = useMemo(
    () =>
      new Map<string, ChartableStage>(
        [...stage.stages.entries()].filter(
          ([_name, subStage]) => subStage.combinedAnalysis.max > defaultHidden,
        ),
      ),
    [stage.stages, defaultHidden],
  );

  const zoomFilteredValues = useMemo(
    () => zoomFilterValues(stage.values),
    [stage.values, zoomFilterValues],
  );

  return stage.combinedAnalysis.max < defaultHidden ? null : (
    <Accordion
      defaultExpanded={stage.combinedAnalysis.max > defaultCollapsed}
      TransitionProps={transitionProps}
    >
      <AccordionSummary expandIcon={<ExpandMoreIcon />}>
        <Typography>
          {stage.name} (med {formatDuration(stage.combinedAnalysis.med)}, avg{' '}
          {formatDuration(stage.combinedAnalysis.avg)})
        </Typography>
      </AccordionSummary>
      <AccordionDetails>
        {stage.values.length === 0 ? (
          <Alert severity="info">No data</Alert>
        ) : (
          <Grid container direction="column">
            <Grid item style={noUserSelect}>
              <ResponsiveContainer width="100%" height={140}>
                <ComposedChart data={zoomFilteredValues} {...zoomProps}>
                  <defs>
                    <linearGradient id="colorDur" x1="0" y1="0" x2="0" y2="1">
                      {fireColors.map(([percent, color]) => (
                        <stop
                          key={percent}
                          offset={percent}
                          stopColor={color}
                          stopOpacity={0.8}
                        />
                      ))}
                    </linearGradient>
                  </defs>
                  {statuses.length > 1 && <Legend payload={legendPayload} />}
                  <CartesianGrid strokeDasharray="3 3" />
                  <XAxis
                    dataKey="__epoch"
                    type="category"
                    ticks={ticks}
                    tickFormatter={tickFormatterX}
                  />
                  <YAxis
                    yAxisId={1}
                    tickFormatter={tickFormatterY}
                    type="number"
                    tickCount={5}
                    name="Duration"
                    domain={domainY}
                  />
                  <YAxis
                    yAxisId={2}
                    orientation="right"
                    type="number"
                    tickCount={5}
                    name="Count"
                  />
                  <Tooltip
                    formatter={tooltipValueFormatter}
                    labelFormatter={labelFormatter}
                  />
                  {statuses.reverse().map(status => (
                    <Fragment key={status}>
                      {!chartTypes[status].includes('duration') ? null : (
                        <>
                          <Area
                            isAnimationActive={false}
                            yAxisId={1}
                            type="monotone"
                            dataKey={status}
                            stackId={status}
                            stroke={
                              statuses.length > 1
                                ? statusColorMap[status]
                                : colorStroke
                            }
                            fillOpacity={statuses.length > 1 ? 0.5 : 1}
                            fill={
                              statuses.length > 1
                                ? statusColorMap[status]
                                : 'url(#colorDur)'
                            }
                            connectNulls
                          />
                          <Line
                            isAnimationActive={false}
                            yAxisId={1}
                            type="monotone"
                            dataKey={`${status} avg`}
                            stroke={
                              statuses.length > 1
                                ? statusColorMap[status]
                                : colorStrokeAvg
                            }
                            opacity={0.8}
                            strokeWidth={2}
                            dot={false}
                            connectNulls
                          />
                        </>
                      )}
                      {!chartTypes[status].includes('count') ? null : (
                        <Bar
                          isAnimationActive={false}
                          yAxisId={2}
                          type="monotone"
                          dataKey={`${status} count`}
                          stackId="1"
                          stroke={statusColorMap[status] ?? ''}
                          fillOpacity={0.5}
                          fill={statusColorMap[status] ?? ''}
                        />
                      )}
                    </Fragment>
                  ))}
                  {getZoomArea({ yAxisId: 1 })}
                </ComposedChart>
              </ResponsiveContainer>
            </Grid>
            {subStages.size === 0 ? null : (
              <Grid item>
                <Accordion
                  defaultExpanded={false}
                  TransitionProps={transitionProps}
                >
                  <AccordionSummary expandIcon={<ExpandMoreIcon />}>
                    <Typography>Sub stages ({subStages.size})</Typography>
                  </AccordionSummary>
                  <AccordionDetails>
                    <div style={fullWidth}>
                      {[...subStages.values()].map(subStage => (
                        <StageChart
                          key={subStage.name}
                          {...chartOptions}
                          stage={subStage}
                        />
                      ))}
                    </div>
                  </AccordionDetails>
                </Accordion>
              </Grid>
            )}
          </Grid>
        )}
      </AccordionDetails>
    </Accordion>
  );
}
Example #22
Source File: BarChartWidget.tsx    From console with GNU Affero General Public License v3.0 4 votes vote down vote up
BarChartWidget = ({
  classes,
  title,
  panelItem,
  timeStart,
  timeEnd,
  propLoading,
  apiPrefix,
  zoomActivated = false,
}: IBarChartWidget) => {
  const dispatch = useDispatch();
  const [loading, setLoading] = useState<boolean>(true);
  const [data, setData] = useState<any>([]);
  const [result, setResult] = useState<IDashboardPanel | null>(null);

  useEffect(() => {
    if (propLoading) {
      setLoading(true);
    }
  }, [propLoading]);

  useEffect(() => {
    if (loading) {
      let stepCalc = 0;
      if (timeStart !== null && timeEnd !== null) {
        const secondsInPeriod = timeEnd.unix() - timeStart.unix();
        const periods = Math.floor(secondsInPeriod / 60);

        stepCalc = periods < 1 ? 15 : periods;
      }

      api
        .invoke(
          "GET",
          `/api/v1/${apiPrefix}/info/widgets/${
            panelItem.id
          }/?step=${stepCalc}&${
            timeStart !== null ? `&start=${timeStart.unix()}` : ""
          }${timeStart !== null && timeEnd !== null ? "&" : ""}${
            timeEnd !== null ? `end=${timeEnd.unix()}` : ""
          }`
        )
        .then((res: any) => {
          const widgetsWithValue = widgetDetailsToPanel(res, panelItem);
          setData(widgetsWithValue.data);
          setResult(widgetsWithValue);
          setLoading(false);
        })
        .catch((err: ErrorResponseHandler) => {
          dispatch(setErrorSnackMessage(err));
          setLoading(false);
        });
    }
  }, [loading, panelItem, timeEnd, timeStart, dispatch, apiPrefix]);

  const barChartConfiguration = result
    ? (result.widgetConfiguration as IBarChartConfiguration[])
    : [];

  let greatestIndex = 0;
  let currentValue = 0;

  if (barChartConfiguration.length === 1) {
    const dataGraph = barChartConfiguration[0];
    data.forEach((item: any, index: number) => {
      if (item[dataGraph.dataKey] > currentValue) {
        currentValue = item[dataGraph.dataKey];
        greatestIndex = index;
      }
    });
  }

  const theme = useTheme();
  const biggerThanMd = useMediaQuery(theme.breakpoints.up("md"));

  return (
    <div className={zoomActivated ? "" : classes.singleValueContainer}>
      {!zoomActivated && (
        <div className={classes.titleContainer}>
          {title} <ExpandGraphLink panelItem={panelItem} />
        </div>
      )}
      {loading && (
        <div className={classes.loadingAlign}>
          <Loader />
        </div>
      )}
      {!loading && (
        <div
          className={
            zoomActivated ? classes.zoomChartCont : classes.contentContainer
          }
        >
          <ResponsiveContainer width="99%">
            <BarChart
              data={data as object[]}
              layout={"vertical"}
              barCategoryGap={1}
            >
              <XAxis type="number" hide />
              <YAxis
                dataKey="name"
                type="category"
                interval={0}
                tick={<CustomizedAxisTick />}
                tickLine={false}
                axisLine={false}
                width={150}
                hide={!biggerThanMd}
                style={{
                  fontSize: "12px",
                  fontWeight: 100,
                }}
              />
              {barChartConfiguration.map((bar) => (
                <Bar
                  key={`bar-${bar.dataKey}`}
                  dataKey={bar.dataKey}
                  fill={bar.color}
                  background={bar.background}
                  barSize={zoomActivated ? 25 : 12}
                >
                  {barChartConfiguration.length === 1 ? (
                    <Fragment>
                      {data.map((_: any, index: number) => (
                        <Cell
                          key={`chart-bar-${index.toString()}`}
                          fill={
                            index === greatestIndex
                              ? bar.greatestColor
                              : bar.color
                          }
                        />
                      ))}
                    </Fragment>
                  ) : null}
                </Bar>
              ))}
              <Tooltip
                cursor={{ fill: "rgba(255, 255, 255, 0.3)" }}
                content={
                  <BarChartTooltip
                    barChartConfiguration={barChartConfiguration}
                  />
                }
              />
            </BarChart>
          </ResponsiveContainer>
        </div>
      )}
    </div>
  );
}
Example #23
Source File: Balances.tsx    From abrechnung with GNU Affero General Public License v3.0 4 votes vote down vote up
export default function Balances({ group }) {
    const theme: Theme = useTheme();
    const isSmallScreen = useMediaQuery(theme.breakpoints.down("sm"));
    const history = useHistory();

    const personalAccounts = useRecoilValue(personalAccountsSeenByUser(group.id));
    const clearingAccounts = useRecoilValue(clearingAccountsSeenByUser(group.id));
    const balances = useRecoilValue(accountBalances(group.id));

    const [selectedTab, setSelectedTab] = useState("1");

    const colorGreen = theme.palette.mode === "light" ? theme.palette.success.light : theme.palette.success.dark;
    const colorRed = theme.palette.mode === "light" ? theme.palette.error.light : theme.palette.error.dark;
    const colorGreenInverted = theme.palette.mode === "dark" ? theme.palette.success.light : theme.palette.success.dark;
    const colorRedInverted = theme.palette.mode === "dark" ? theme.palette.error.light : theme.palette.error.dark;

    useTitle(`${group.name} - Balances`);

    const chartData = personalAccounts.map((account) => {
        return {
            name: account.name,
            balance: balances[account.id].balance,
            totalPaid: balances[account.id].totalPaid,
            totalConsumed: balances[account.id].totalConsumed,
            id: account.id,
        };
    });

    const unbalancedClearingAccounts = clearingAccounts
        .filter((account) => balances[account.id].balance !== 0)
        .map((account) => {
            return {
                name: account.name,
                id: account.id,
                balance: balances[account.id].balance,
            };
        });

    const chartHeight = Object.keys(balances).length * 30 + 100;

    // TODO determine the rendered width of the account names and take the maximum
    const yaxiswidth = isSmallScreen
        ? Math.max(Math.max(...personalAccounts.map((account) => account.name.length)), 20)
        : Math.max(...personalAccounts.map((account) => account.name.length)) * 7 + 5;

    const handleBarClick = (data, event) => {
        const id = data.activePayload[0].payload.id;
        history.push(`/groups/${group.id}/accounts/${id}`);
    };

    return (
        <MobilePaper>
            <TabContext value={selectedTab}>
                <Box sx={{ borderBottom: 1, borderColor: "divider" }}>
                    <TabList onChange={(event, idx) => setSelectedTab(idx)} centered>
                        <Tab label="Chart" value="1" />
                        <Tab label="Table" value="2" />
                    </TabList>
                </Box>
                <TabPanel value="1" sx={{ padding: { xs: 1, md: 2 } }}>
                    {personalAccounts.length === 0 && <Alert severity="info">No Accounts</Alert>}
                    {unbalancedClearingAccounts.length !== 0 && (
                        <Alert severity="info">
                            <AlertTitle>Some Clearing Accounts have remaining balances.</AlertTitle>
                            {unbalancedClearingAccounts.map((account) => (
                                <Typography variant="body2" key={account.id} component="span">
                                    <>{account.name}:</>
                                    <Typography
                                        variant="body2"
                                        component="span"
                                        sx={{ color: account.balance < 0 ? colorRedInverted : colorGreenInverted }}
                                    >
                                        {account.balance.toFixed(2)} {group.currency_symbol}{" "}
                                    </Typography>
                                </Typography>
                            ))}
                        </Alert>
                    )}
                    {isSmallScreen ? (
                        <List>
                            {personalAccounts.map((account) => (
                                <>
                                    <ListItemLink key={account.id} to={`/groups/${group.id}/accounts/${account.id}`}>
                                        <ListItemText primary={account.name} />
                                        <Typography
                                            align="right"
                                            variant="body2"
                                            sx={{
                                                color:
                                                    balances[account.id].balance < 0
                                                        ? colorRedInverted
                                                        : colorGreenInverted,
                                            }}
                                        >
                                            {balances[account.id].balance.toFixed(2)} {group.currency_symbol}
                                        </Typography>
                                    </ListItemLink>
                                    <Divider key={account.id * 2} component="li" />
                                </>
                            ))}
                        </List>
                    ) : (
                        <div className="area-chart-wrapper" style={{ width: "100%", height: `${chartHeight}px` }}>
                            <ResponsiveContainer>
                                <BarChart
                                    data={chartData}
                                    margin={{
                                        top: 20,
                                        right: 20,
                                        bottom: 20,
                                        left: 20,
                                    }}
                                    layout="vertical"
                                    onClick={handleBarClick}
                                >
                                    <XAxis
                                        stroke={theme.palette.text.primary}
                                        type="number"
                                        unit={group.currency_symbol}
                                    />
                                    <YAxis
                                        dataKey="name"
                                        stroke={theme.palette.text.primary}
                                        type="category"
                                        width={yaxiswidth}
                                    />
                                    <Tooltip
                                        formatter={(label) =>
                                            parseFloat(label).toFixed(2) + ` ${group.currency_symbol}`
                                        }
                                        labelStyle={{
                                            color: theme.palette.text.primary,
                                        }}
                                        itemStyle={{
                                            color: theme.palette.text.primary,
                                        }}
                                        contentStyle={{
                                            backgroundColor: theme.palette.background.paper,
                                            borderColor: theme.palette.divider,
                                            borderRadius: theme.shape.borderRadius,
                                        }}
                                    />
                                    <Bar dataKey="balance">
                                        {chartData.map((entry, index) => {
                                            return (
                                                <Cell
                                                    key={`cell-${index}`}
                                                    fill={entry["balance"] >= 0 ? colorGreen : colorRed}
                                                />
                                            );
                                        })}
                                        <LabelList
                                            dataKey={(entry) =>
                                                `${entry["balance"].toFixed(2)}${group.currency_symbol}`
                                            }
                                            position="insideLeft"
                                            fill={theme.palette.text.primary}
                                        />
                                    </Bar>
                                </BarChart>
                            </ResponsiveContainer>
                        </div>
                    )}
                </TabPanel>
                <TabPanel value="2" sx={{ padding: { xs: 1, md: 2 } }}>
                    <BalanceTable group={group} />
                </TabPanel>
            </TabContext>
        </MobilePaper>
    );
}
Example #24
Source File: BasketStats.tsx    From mStable-apps with GNU Lesser General Public License v3.0 4 votes vote down vote up
BasketStats: FC<{ simulation?: MassetState }> = ({ simulation }) => {
  const masset = useSelectedMassetState()
  // eslint-disable-next-line
  const bAssets: MassetState['bAssets'] = simulation?.bAssets ?? masset?.bAssets ?? {}

  const data: Datum[] = useMemo(
    () =>
      Object.values(bAssets).map(({ basketShare, maxWeight, token: { symbol }, overweight, totalVault }) => {
        const basketShareAsPercentage = basketShare.toPercent()
        const maxWeightAsPercentage = new BigDecimal(maxWeight ?? '0', 18).toPercent()

        // Get the remainder so that it can be stacked after the basket share
        const remainderMaxWeight = parseFloat(
          (basketShareAsPercentage > maxWeightAsPercentage ? 0 : maxWeightAsPercentage - basketShareAsPercentage).toFixed(2),
        )

        return {
          symbol,
          basketShareAsPercentage,
          maxWeightAsPercentage,
          remainderMaxWeight,
          overweight,
          vaultBalance: toK(totalVault.simple),
          fill: overweight ? OVERWEIGHT_TOKEN_COLOURS[symbol as TokenSymbol] : TOKEN_COLOURS[symbol as TokenSymbol],
        }
      }),
    [bAssets],
  )

  return (
    <Container>
      {data && data.length ? (
        <ResponsiveContainer aspect={1.5} width={250}>
          <BarChart layout="vertical" margin={{ top: 0, right: 0, bottom: 0, left: 0 }} barCategoryGap={1} data={data}>
            <defs>
              {Object.values(bAssets).map(b => (
                <Hatch key={b.token.symbol} symbol={b.token.symbol as TokenSymbol} />
              ))}
            </defs>
            <Tooltip
              cursor={false}
              separator=" "
              contentStyle={{
                fontSize: '14px',
                padding: '8px',
                background: 'rgba(255, 255, 255, 0.8)',
                textAlign: 'right',
                border: 'none',
                borderRadius: '4px',
                color: Color.black,
              }}
              content={CustomTooltip}
              wrapperStyle={{
                top: 0,
                left: 0,
              }}
            />
            <XAxis type="number" unit="%" padding={{ left: 24 }} axisLine={false} />
            <YAxis
              type="category"
              dataKey="symbol"
              tickCount={data.length}
              minTickGap={0}
              axisLine={false}
              tick={({
                payload: { value },
                x,
                y,
                height,
              }: {
                payload: {
                  value: TokenSymbol
                }
                x: number
                y: number
                height: number
              }) => {
                const diameter = (height - data.length * 6) / data.length
                return (
                  <TokenIconSvg x={x - diameter / 2} y={y - diameter / 2} height={diameter} width={diameter} symbol={value} key={value} />
                ) as unknown as SVGElement
              }}
            />
            <Bar dataKey="basketShareAsPercentage" name="Basket share" unit="%" stackId="a" />
            <Bar dataKey="remainderMaxWeight" name="Max weight" unit="%" stackId="a">
              {data.map(({ symbol }) => (
                <Cell key={symbol} fill={`url(#hatch-${symbol})`} />
              ))}
            </Bar>
            )
          </BarChart>
        </ResponsiveContainer>
      ) : (
        <ThemedSkeleton height={132} />
      )}
    </Container>
  )
}
Example #25
Source File: index.tsx    From korona-info with MIT License 4 votes vote down vote up
Index: NextPage<{ groupedCoronaData: GroupedData, hospitalised: HospitalData[] }> = ({
  groupedCoronaData, hospitalised
}: {
  groupedCoronaData: GroupedData;
  hospitalised: HospitalData[];
}) => {
  const [selectedHealthCareDistrict, selectHealthCareDistrict] = useState<
    string
  >('all');
  const confirmed = groupedCoronaData[selectedHealthCareDistrict].confirmed;
  const deaths = groupedCoronaData[selectedHealthCareDistrict].deaths;
  const recovered = groupedCoronaData[selectedHealthCareDistrict].recovered;
  const allConfirmed = groupedCoronaData.all.confirmed;
  const toast = useToast()
  const latestInfection = confirmed.length
    ? format(
      utcToZonedTime(
        new Date(confirmed[confirmed.length - 1].date),
        timeZone
      ),
      'dd.MM.yyyy - HH:mm',
      { timeZone }
    )
    : null;
  const latestInfectionDistrict =
    confirmed[confirmed.length - 1]?.healthCareDistrict;
  const latestDeath = deaths.length
    ? format(
      utcToZonedTime(new Date(deaths[deaths.length - 1].date), timeZone),
      'd.M.yyyy'
    )
    : null;
  const latestDeathDistrict = deaths.length
    ? deaths[deaths.length - 1].area
    : null;
  const latestRecoveredDistrict = recovered.length
    ? recovered[recovered.length - 1].healthCareDistrict
    : null;
  const latestRecovered = recovered.length
    ? format(
      utcToZonedTime(
        new Date(recovered[recovered.length - 1].date),
        timeZone
      ),
      'd.M.yyyy'
    )
    : null;
  const infectionsToday = getInfectionsToday(confirmed);

  const [cumulativeChartScale, setCumulativeChartScale] = useState<
    'linear' | 'log'
  >('linear');
  const [forecastChartScale, setForecaseChartScale] = useState<
    'linear' | 'log'
  >('linear');
  // Map data to show development of infections
  const {
    infectionDevelopmentData,
    infectionDevelopmentData30Days
  } = groupedCoronaData[selectedHealthCareDistrict].timeSeries;
  const maxValues =
    infectionDevelopmentData30Days[infectionDevelopmentData30Days.length - 1];
  const dataMaxValue = Math.max(
    maxValues?.deaths ?? 0,
    maxValues?.infections ?? 0,
    maxValues?.infections ?? 0
  );

  const {
    infectionsByDistrict,
    infectionsByDistrictPercentage,
    areas
  } = getTnfectionsByDistrict(allConfirmed);
  const { infectionsBySourceCountry } = getInfectionsBySourceCountry(confirmed);
  const networkGraphData = getNetworkGraphData(confirmed);

  const { t } = useContext(UserContext);

  const humanizeHealthcareDistrict = (district: string) => {
    if (district === 'all') {
      return t('All healthcare districts');
    } else if (district === 'unknown') {
      return t('unknown');
    } else {
      return district;
    }
  };

  const reversedConfirmed = confirmed
    // @ts-ignore
    .map((i, index) => ({
      index: index + 1,
      ...i,
      healthCareDistrict: humanizeHealthcareDistrict(i.healthCareDistrict)
    }))
    .reverse();

  const humanizedHealthCareDistrict = humanizeHealthcareDistrict(
    selectedHealthCareDistrict
  );
  useEffect(() => {
    if (typeof window !== undefined) {

      toast({
        position: 'bottom',
        title: 'Datan lähteenä nyt THL',
        description: 'HS:n datan lähde on vaihtunut THL:ään. THL:n tiedotussyklistä johtuen tiedot päivittyvät aiempaa harvemmin. Myös vanhemmissa tapauksissa voi olla päivämääräkohtaisia eroja, johtuen muuttuneesta raportointitavasta.',
        status: "info",
        isClosable: true,
        duration: 14000,
      });




    }

  }, [])

  return (
    <>
      <Head>
        <title>
          {t('finland corona status')} - {t('cases')} : {confirmed.length || 0}{' '}
          - {t('recovered')}: {recovered.length || 0} - {t('deaths')}:{' '}
          {deaths.length || 0}
        </title>
        <meta
          name="description"
          content={`Suomen koronavirus-tartuntatilanne – tartunnat: ${confirmed.length ||
            0} - parantuneet: ${recovered.length ||
            0} - menehtyneet: ${deaths.length || 0}`}
        />
        <meta property="og:title" content={t('finland corona status')} />
        <meta
          property="og:description"
          content={`Tartuntoja tällä hetkellä: ${confirmed.length ||
            0} - parantuneet: ${recovered.length ||
            0} - menehtyneet: ${deaths.length || 0}`}
        />
        <meta
          property="og:site_name"
          content="Suomen koronavirus-tartuntatilanne"
        />
        <meta property="og:locale" content="fi_FI" />
        <meta property="og:type" content="website" />
        <meta property="og:image" content="/images/corona-virus.png" />
        <meta property="og:image:width" content="1920" />
        <meta property="og:image:height" content="1928" />
        <meta property="og:url" content="https://korona.kans.io" />
      </Head>
      <Layout>

        <Flex
          alignItems="center"
          flexDirection="column"
          flex="1"
          width={'100%'}
          maxWidth="1440px"
          margin="auto"
        >
          <Header />
          <Flex
            flexWrap="wrap"
            flexDirection="row"
            justifyContent="left"
            alignItems="stretch"
            flex="1"
            width={'100%'}
          >
            <Box width={['100%', '100%', 1 / 3, 1 / 3]} p={3}>
              <Select
                value={selectedHealthCareDistrict ?? undefined}
                onChange={event => selectHealthCareDistrict(event.target.value)}
              >
                <option key={'all'} value={'all'}>
                  {t('All healthcare districts')}
                </option>
                {healtCareDistricts.map(healthcareDistrict => (
                  <option
                    key={healthcareDistrict.name}
                    value={healthcareDistrict.name}
                  >
                    {healthcareDistrict.name}
                  </option>
                ))}
              ))}
            </Select>
            </Box>
          </Flex>
          <Flex
            flexWrap="wrap"
            flexDirection="row"
            justifyContent="center"
            alignItems="stretch"
            flex="1"
            width={'100%'}
          >
            <Box width={['100%', '100%', 1 / 2, 1 / 2]} p={3}>
              <Block
                title={t('cases') + ` (${humanizedHealthCareDistrict})`}
                textAlign="center"
                extraInfo={`${t('New cases today')} ${infectionsToday} ${t(
                  'person'
                )}`}
                footer={`${t(
                  'latest case'
                )} ${latestInfection} (${humanizeHealthcareDistrict(
                  latestInfectionDistrict
                )})`}
              >
                <StatBlock
                  count={confirmed.length}
                  helpText={`${t('New cases today')}: ${infectionsToday} ${t(
                    'person'
                  )}`}
                />
              </Block>
            </Box>
            <Box width={['100%', '100%', 1 / 2, 1 / 2]} p={3}>
              <Block
                title={t('deaths') + ` (${humanizedHealthCareDistrict})`}
                footer={
                  latestDeath
                    ? `${t(
                      'last death'
                    )} ${latestDeath} (${humanizeHealthcareDistrict(
                      latestDeathDistrict!
                    )})`
                    : t('no death')
                }
              >
                <StatBlock count={deaths.length || 0} />
              </Block>
            </Box>
            {/* <Box width={['100%', '100%', 1 / 3, 1 / 3]} p={3}>
              <Block
                title={t('recovered') + ` (${humanizedHealthCareDistrict})`}
                footer={
                  `${latestRecovered
                    ? `${t(
                      'latest recovery'
                    )} ${latestRecovered} (${humanizeHealthcareDistrict(latestRecoveredDistrict!)}).`
                    : ' '} ${t('recoveredNotice')}`}
              >
                <StatBlock count={recovered.length || 0} />
              </Block>
            </Box> */}

            <Box width={['100%']} p={3}>
              <Block
                title={
                  t('accumulated change') + ` (${humanizedHealthCareDistrict})`
                }
                footer={t('cases recovered and death in past 30 days')}
              >
                <ButtonGroup
                  spacing={0}
                  alignSelf="center"
                  display="flex"
                  justifyContent="center"
                  marginTop="-15px"
                >
                  <Button
                    size="xs"
                    fontFamily="Space Grotesk Regular"
                    px={3}
                    letterSpacing="1px"
                    borderRadius="4px 0px 0px 4px"
                    borderWidth="0px"
                    isActive={cumulativeChartScale === 'linear'}
                    onClick={() => setCumulativeChartScale('linear')}
                  >
                    {t('linear')}
                  </Button>
                  <Button
                    size="xs"
                    fontFamily="Space Grotesk Regular"
                    px={3}
                    letterSpacing="1px"
                    borderRadius="0px 4px 4px 0px"
                    borderWidth="0px"
                    isActive={cumulativeChartScale === 'log'}
                    onClick={() => setCumulativeChartScale('log')}
                  >
                    {t('logarithmic')}
                  </Button>
                </ButtonGroup>
                <ResponsiveContainer width={'100%'} height={380}>
                  <ComposedChart
                    data={
                      cumulativeChartScale === 'log'
                        ? infectionDevelopmentData30Days.map(zerosToNulls)
                        : infectionDevelopmentData30Days
                    }
                    margin={{ top: 20, right: 30, left: 0, bottom: 30 }}
                  >
                    <defs>
                      <linearGradient
                        id="colorInfection"
                        x1="0"
                        y1="0"
                        x2="0"
                        y2="1"
                      >
                        <stop
                          offset="5%"
                          stopColor={colors[8]}
                          stopOpacity={0.6}
                        />
                        <stop
                          offset="95%"
                          stopColor={colors[8]}
                          stopOpacity={0}
                        />
                      </linearGradient>
                      <linearGradient
                        id="colorRecovered"
                        x1="0"
                        y1="0"
                        x2="0"
                        y2="1"
                      >
                        <stop
                          offset="5%"
                          stopColor={colors[7]}
                          stopOpacity={0.6}
                        />
                        <stop
                          offset="95%"
                          stopColor={colors[7]}
                          stopOpacity={0}
                        />
                      </linearGradient>
                      <linearGradient
                        id="colorDeaths"
                        x1="0"
                        y1="0"
                        x2="0"
                        y2="1"
                      >
                        <stop
                          offset="5%"
                          stopColor={colors[0]}
                          stopOpacity={0.6}
                        />
                        <stop
                          offset="95%"
                          stopColor={colors[0]}
                          stopOpacity={0}
                        />
                      </linearGradient>
                    </defs>
                    <XAxis
                      tickFormatter={d => format(new Date(d), 'd.M.')}
                      tick={<CustomizedAxisTick isDate />}
                      dataKey="date"
                      domain={['dataMin', 'dataMax']}
                      type="number"
                      scale="time"
                    />
                    <YAxis
                      scale={cumulativeChartScale}
                      dataKey="infections"
                      domain={[
                        cumulativeChartScale === 'log' ? 1 : 0,
                        dataMaxValue + 10
                      ]}
                      unit={' ' + t('person')}
                      tick={{ fontSize: 12 }}
                      name={t('cases')}
                    />
                    <CartesianGrid opacity={0.2} />
                    <Tooltip
                      labelFormatter={v => format(new Date(v), 'dd.MM.yyyy')}
                    />
                    <Bar
                      isAnimationActive={false}
                      fill={colors[1]}
                      opacity={0.4}
                      dataKey="infectionsDaily"
                      name={t('cases of the day')}
                      unit={' ' + t('person')}
                    />
                    <Area
                      isAnimationActive={false}
                      type="monotone"
                      unit={' ' + t('person')}
                      name={t('total cases')}
                      dataKey="infections"
                      stroke={colors[8]}
                      fillOpacity={1}
                      fill="url(#colorInfection)"
                    />
                    {/* <Area
                      isAnimationActive={false}
                      type="monotone"
                      unit={' ' + t('person')}
                      name={t('total recovered')}
                      dataKey="recovered"
                      stroke={colors[7]}
                      fillOpacity={1}
                      fill="url(#colorRecovered)"
                    /> */}
                    <Area
                      isAnimationActive={false}
                      type="monotone"
                      unit={' ' + t('person')}
                      name={t('total deaths')}
                      dataKey="deaths"
                      stroke={colors[0]}
                      fillOpacity={1}
                      fill="url(#colorDeaths)"
                    />
                    <Legend wrapperStyle={{ bottom: '10px' }} />
                  </ComposedChart>
                </ResponsiveContainer>
              </Block>
            </Box>
            {/*
          <Box width={['100%']} p={3}>
            <Block title="Tartuntojen kumulatiivinen ennustemalli" footer={`Tartuntojen kehityksen ennustemalli 60 päivää. Laskee ennustetun eksponentiaalisen kasvun käyttämällä aiemmin luotuja tietoja.  Käytetty <a style="color: #319795;" href="https://github.com/mljs/regression-exponential" target="_blank">exponential-regression</a> kirjastoa.`}>
              <ButtonGroup spacing={0} alignSelf="center" display="flex" justifyContent="center" marginTop="-15px">
                <Button size="xs" fontFamily="Space Grotesk Regular" px={3} letterSpacing="1px" borderRadius="4px 0px 0px 4px" borderWidth="0px" isActive={forecastChartScale === 'linear'} onClick={() => setForecaseChartScale('linear')}>
                  Lineaarinen
                </Button>
                <Button size="xs" fontFamily="Space Grotesk Regular" px={3} letterSpacing="1px" borderRadius="0px 4px 4px 0px" borderWidth="0px" isActive={forecastChartScale === 'log'}  onClick={() => setForecaseChartScale('log')}>
                  Logaritminen
                </Button>
              </ButtonGroup>
              <ResponsiveContainer width={'100%'} height={350}>
                <AreaChart
                    data={prediction60Days}
                    margin={{ top: 20, right: 30, left: 0, bottom: 20 }}
                >
                  <defs>
                    <linearGradient id="colorInfection" x1="0" y1="0" x2="0" y2="1">
                      <stop offset="5%" stopColor={colors[8]} stopOpacity={0.6} />
                      <stop offset="95%" stopColor={colors[8]} stopOpacity={0} />
                    </linearGradient>
                  </defs>
                  <XAxis tickFormatter={d => format(new Date(d), 'd.M.')} tick={<CustomizedAxisTick isDate />} dataKey="date" domain={['dataMin', 'dataMax']} type="number" scale="time" />
                  <YAxis scale={forecastChartScale} dataKey="infections" domain={['auto', 'auto']} unit={' ' + t('person') } tick={{ fontSize: 12 }} name="Tartunnat" />

                  <CartesianGrid opacity={0.2} />
                  <ReferenceLine
                    x={today}
                    stroke="rgba(0,0,0,.5)"
                    // @ts-ignore
                    label={{ position: 'top', value: 'Nyt', fill: 'rgba(0,0,0,0.5)', fontSize: 12 }}
                    strokeDasharray="3 3" />
                  <Tooltip labelFormatter={v => format(new Date(v), 'dd.MM.yyyy')} />
                  <Area type="monotone" name="Ennuste" unit={' ' + t('person') } dataKey="infections" stroke={colors[8]} fillOpacity={1} fill="url(#colorInfection)" />
                </AreaChart>
              </ResponsiveContainer>
            </Block>
          </Box>
           */}
            <Box width={['100%', '100%', '100%', '100%', 1 / 2]} p={3}>
              <Block
                title={t('Cases by district')}
                footer={t('Helsinki metropolitan area is shown as HUS')}
              >
                <ResponsiveContainer width={'100%'} height={350}>
                  <BarChart
                    data={infectionsByDistrict}
                    margin={{
                      top: 20,
                      right: 30,
                      left: 0,
                      bottom: 85
                    }}
                  >
                    <XAxis
                      interval={0}
                      dataKey="name"
                      tick={<CustomizedAxisTick />}
                    />
                    <YAxis
                      yAxisId="left"
                      unit={' ' + t('person')}
                      dataKey="infections"
                      tick={{ fontSize: 12 }}
                    />
                    <Tooltip />
                    <Bar
                      isAnimationActive={false}
                      dataKey="infections"
                      name={t('cases')}
                      unit={' ' + t('person')}
                      yAxisId="left"
                    >
                      {areas.map((area, index) => (
                        <Cell key={area} fill={colors[index % colors.length]} />
                      ))}
                      <LabelList
                        dataKey="infections"
                        position="top"
                        formatter={e => e}
                      />
                    </Bar>
                  </BarChart>
                </ResponsiveContainer>
              </Block>
            </Box>
            <Box width={['100%', '100%', '100%', '100%', 1 / 2]} p={3}>
              <Block
                title={t('infectionsPerDisrictAndSize')}
                footer={t('infectionsPerDisrictAndSize')}
              >
                <ResponsiveContainer width={'100%'} height={350}>
                  <BarChart
                    data={infectionsByDistrictPercentage}
                    margin={{
                      top: 20,
                      right: 30,
                      left: 0,
                      bottom: 85
                    }}
                  >
                    <XAxis
                      interval={0}
                      dataKey="name"
                      tick={<CustomizedAxisTick />}
                    />
                    <YAxis
                      unit=" %"
                      dataKey="perDistrict"
                      tick={{ fontSize: 12 }}
                    />
                    <Tooltip />
                    <Bar isAnimationActive={false} dataKey="perDistrict" name="%-osuus väestöstä" unit=" %">
                      {areas.map((area, index) => (
                        <Cell key={area} fill={colors[index % colors.length]} />
                      ))}
                      <LabelList
                        dataKey="perDistict"
                        position="top"
                        formatter={e => e}
                      />
                    </Bar>
                  </BarChart>
                </ResponsiveContainer>
              </Block>
            </Box>
            <Box width={['100%', '100%', '100%', '100%', 1 / 2]} p={3}>
              <Block
                title={t('log') + ` (${humanizedHealthCareDistrict})`}
                footer={t('logFooter')}
              >
                <Table
                  height={500}
                  data={reversedConfirmed}
                  columns={useMemo(() => infectionColumns, [])}
                />
              </Block>
            </Box>
            <BubbleChart data={groupedCoronaData} />
            {/* <Box width={['100%', '100%', '100%', '100%', 1 / 2]} p={3}>
              <Block
                title={
                  t('infectionNetwork') + ` (${humanizedHealthCareDistrict})`
                }
                footer={t('infectionNetworkFooter')}
              >
                <NetworkGraph data={networkGraphData} />
              </Block>
            </Box> */}
            <Box width={['100%']} p={3}>
              <Block
                title={
                  t('hospitalizedData') + ` (${t('All healthcare districts')})`
                }
              >
                <ResponsiveContainer width={'100%'} height={350}>
                  <BarChart
                    data={hospitalised.slice(Math.max(hospitalised.length - 30, 0))}
                    margin={{
                      top: 20,
                      right: 30,
                      left: 0,
                      bottom: 85
                    }}
                  >
                    <XAxis
                      interval={0}
                      dataKey="dateString"
                      tick={<CustomizedAxisTick />}
                      padding={{ left: 50, right: 50 }}
                    />
                    <YAxis
                      unit={' ' + t('person')}
                      dataKey="totalHospitalised"
                      tick={{ fontSize: 12 }}
                    />
                    <Tooltip />
                    <Bar
                      isAnimationActive={false}
                      stackId="a"
                      dataKey="inIcu"
                      name={t("inIcu")}
                      unit={' ' + t('person')}
                      fill="#F3858D"
                    />
                    <Bar
                      isAnimationActive={false}
                      stackId="a"
                      dataKey="inWard"
                      name={t("inWard")}
                      unit={' ' + t('person')}
                      fill="#2FAB8E"
                    />

                    <Bar
                      isAnimationActive={false}
                      stackId="a"
                      dataKey="totalHospitalised"
                      opacity={0}
                      name={t("inHospital")}
                      unit={' ' + t('person')}
                      fill="rgba(0,0,0,1)"
                      strokeWidth={0}
                      legendType="none"
                    />
                    <Legend wrapperStyle={{ bottom: '15px' }} />
                  </BarChart>
                </ResponsiveContainer>
              </Block>
            </Box>

          </Flex>

          <Copyright />
        </Flex>
      </Layout>
    </>
  );
}
Example #26
Source File: Profile.tsx    From avalon.ist with MIT License 4 votes vote down vote up
render() {
    const { initialHeight } = this;
    const {
      myname,
      style: { themeLight },
    } = this.props.match.params;

    const theme = themeLight ? 'light' : 'dark';
    const data: any[] = [];

    // const { avatarStyle } = this.props.match.params.style;
    const {
      username,
      nationality,
      bio,
      gameRating,
      gameHistory,
      games,
      gameStats,
      gameShots,
      avatars,
      showSpy,
      redirect,
    } = this.state;

    for (const k in gameStats) {
      const stat = gameStats[k];

      data.push({
        name: k.charAt(0).toUpperCase() + k.slice(1),
        wins: stat[0],
        total: stat[1],
        Winrate: stat[1] === 0 ? 0 : Percent(stat[0] / stat[1]),
        color: SPY_ROLES.has(k) ? '#ff6384' : '#36a2eb',
      });
    }

    const country = countries.find((c) => c.text === nationality);
    const totalWon = games[0];
    const totalLost = games[1] - totalWon;
    const winRate = games[1] > 0 ? Percent(totalWon / games[1]) : 0;
    const shotRate = gameShots[1] > 0 ? Percent(gameShots[0] / gameShots[1]) : 0;

    let countryFlag = <img alt={'UN'} src={UN_FLAG} />;
    if (country && country.value != 'UN') {
      if (country.value == 'LGBT') {
        countryFlag = <img alt={'Stonewall'} src={STONEWALL_FLAG} />;
      } else {
        countryFlag = <Flag code={country.value} />;
      }
    }

    return redirect ? (
      <Redirect to="/profile-not-found" />
    ) : (
      <div id="Background-2" className={`full ${theme}`}>
        <Navbar username="" key={'Navbar'} />
        <AvalonScrollbars>
          <div id="Profile" style={{ minHeight: `${initialHeight}px` }}>
            <div className="row">
              <div id="user">
                <img
                  src={showSpy ? avatars.spy : avatars.res}
                  alt={'Avatar'}
                  onMouseOver={this.onHover}
                  onMouseLeave={this.onStopHover}
                />
                <div className="user-tag">
                  {countryFlag}
                  <p>
                    <b>{username}</b>
                    <br />
                    {nationality}
                  </p>
                </div>
              </div>
              <div id="bio" className="bubble">
                <AvalonScrollbars>
                  <ReactMarkdown
                    className="markdown"
                    allowedTypes={[
                      'text',
                      'paragraph',
                      'emphasis',
                      'strong',
                      'thematicBreak',
                      'blockquote',
                      'list',
                      'listItem',
                      'heading',
                    ]}
                  >
                    {bio}
                  </ReactMarkdown>
                </AvalonScrollbars>
              </div>
            </div>
            <div className="row">
              <div id="stats">
                <h1>STATISTICS</h1>
                <table>
                  <tbody>
                    <tr>
                      <th>Statistic</th>
                      <th>Value</th>
                    </tr>
                    <tr>
                      <td>Total Games Played</td>
                      <td>{games[1]}</td>
                    </tr>
                    <tr>
                      <td>Total Games Won</td>
                      <td>{totalWon}</td>
                    </tr>
                    <tr>
                      <td>Total Games Lost</td>
                      <td>{totalLost}</td>
                    </tr>
                    <tr>
                      <td>Total Win Rate</td>
                      <td>{winRate}%</td>
                    </tr>
                    <tr>
                      <td>Shot Accuracy</td>
                      <td>{shotRate}%</td>
                    </tr>
                    <tr>
                      <td>Rating</td>
                      <td>{gameRating}</td>
                    </tr>
                  </tbody>
                </table>
              </div>
              <div id="graph">
                <ResponsiveContainer width={'100%'} height={300}>
                  <BarChart
                    layout="vertical"
                    margin={{
                      top: 20,
                      right: 20,
                      bottom: 20,
                      left: 20,
                    }}
                    data={data}
                  >
                    <CartesianGrid strokeDasharray="1 1" />
                    <XAxis type="number" domain={[0, 100]} />
                    <YAxis type="category" width={100} dataKey="name" />
                    <Tooltip content={<CustomTooltip />} />
                    <Bar dataKey="Winrate" fill="#8884d8">
                      {data.map((entry, index) => (
                        <Cell key={`cell-${index}`} fill={entry.color} />
                      ))}
                    </Bar>
                  </BarChart>
                </ResponsiveContainer>
              </div>
            </div>
            <div className="row">
              <div id="history">
                <h1>GAME HISTORY</h1>
                <table>
                  <tbody>
                    <tr>
                      <th>Game</th>
                      <th>Role</th>
                      <th>Size</th>
                      <th>Winner</th>
                      <th>Date</th>
                    </tr>
                    {gameHistory
                      .slice(-10)
                      .reverse()
                      .map((g: any, i) => {
                        const date = new Date(g.date);
                        const month = ('00' + (date.getUTCMonth() + 1)).slice(-2);
                        const day = ('00' + date.getUTCDate()).slice(-2);
                        const year = date.getUTCFullYear();

                        return (
                          <tr key={'Game' + g.id}>
                            <td>
                              <Link to={'/game/' + g.id}>#{g.code}</Link>
                            </td>
                            <td>{g.role}</td>
                            <td>{g.size}</td>
                            <td>{g.winner ? 'Resistance' : 'Spy'}</td>
                            <td>
                              {year}-{month}-{day}
                            </td>
                          </tr>
                        );
                      })}
                  </tbody>
                </table>
              </div>
            </div>
          </div>
        </AvalonScrollbars>
        {myname === username ? (
          <button
            className="button-b edit-your-profile-with-this"
            type="button"
            onClick={this.onFormToggle}
          >
            <p>Edit Profile</p>
          </button>
        ) : null}
        {this.state.showForm ? (
          <EditProfile
            onExit={this.onFormToggle}
            text="Submit"
            nationality={nationality}
            bio={bio}
            title="EDIT YOUR PROFILE"
            onSelect={this.onEdit}
          />
        ) : null}
      </div>
    );
  }