mirror of https://github.com/artifacthub/hub.git
Improve stats page (#1530)
Signed-off-by: Cintia Sanchez Garcia <cynthiasg@icloud.com>
This commit is contained in:
parent
b95e45313d
commit
a39d407bd8
|
|
@ -6,7 +6,7 @@
|
|||
"@analytics/google-analytics": "^0.3.1",
|
||||
"@apidevtools/json-schema-ref-parser": "^9.0.6",
|
||||
"analytics": "^0.3.5",
|
||||
"apexcharts": "^3.26.0",
|
||||
"apexcharts": "^3.27.3",
|
||||
"bootstrap": "^4.5.2",
|
||||
"classnames": "^2.2.6",
|
||||
"codemirror": "^5.57.0",
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
"lodash": "^4.17.21",
|
||||
"moment": "^2.27.0",
|
||||
"react": "^16.13.1",
|
||||
"react-apexcharts": "^1.3.7",
|
||||
"react-apexcharts": "^1.3.9",
|
||||
"react-codemirror2": "^7.2.1",
|
||||
"react-color": "^2.19.3",
|
||||
"react-dom": "^16.13.1",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,249 @@
|
|||
import moment from 'moment';
|
||||
import React, { useEffect } from 'react';
|
||||
import ReactApexChart from 'react-apexcharts';
|
||||
|
||||
import useBreakpointDetect from '../../hooks/useBreakpointDetect';
|
||||
import getMetaTag from '../../utils/getMetaTag';
|
||||
import prettifyNumber from '../../utils/prettifyNumber';
|
||||
|
||||
interface Props {
|
||||
series: any[];
|
||||
id: string;
|
||||
title: string;
|
||||
activeTheme: string;
|
||||
}
|
||||
|
||||
const BrushChart = (props: Props) => {
|
||||
const primaryColor = getMetaTag('primaryColor');
|
||||
const secondaryColor = getMetaTag('secondaryColor');
|
||||
const point = useBreakpointDetect();
|
||||
|
||||
useEffect(() => {
|
||||
// We force to use original selection after changing breakpoint
|
||||
ApexCharts.exec(
|
||||
`${props.id}BrushChart`,
|
||||
'updateOptions',
|
||||
{
|
||||
selection: {
|
||||
xaxis: {
|
||||
min: moment().subtract(12, 'months').valueOf(), // We select last year
|
||||
max: moment.now(),
|
||||
},
|
||||
},
|
||||
},
|
||||
false,
|
||||
true
|
||||
);
|
||||
}, [point, props.id]);
|
||||
|
||||
const getBarChartConfig = (): ApexCharts.ApexOptions => {
|
||||
return {
|
||||
chart: {
|
||||
id: `${props.id}BarChart`,
|
||||
height: 300,
|
||||
type: 'bar',
|
||||
zoom: {
|
||||
enabled: false,
|
||||
},
|
||||
redrawOnWindowResize: false,
|
||||
fontFamily: "'Lato', Roboto, 'Helvetica Neue', Arial, sans-serif !default",
|
||||
toolbar: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
grid: { borderColor: 'var(--border-md)' },
|
||||
plotOptions: {
|
||||
bar: {
|
||||
borderRadius: 5,
|
||||
dataLabels: {
|
||||
position: 'top',
|
||||
},
|
||||
},
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: true,
|
||||
offsetY: -20,
|
||||
style: {
|
||||
fontSize: '12px',
|
||||
colors: ['var(--color-font)'],
|
||||
},
|
||||
formatter: (value: number) => {
|
||||
return prettifyNumber(value);
|
||||
},
|
||||
},
|
||||
colors: ['var(--color-1-500)'],
|
||||
xaxis: {
|
||||
type: 'datetime',
|
||||
position: 'bottom',
|
||||
axisBorder: {
|
||||
show: false,
|
||||
},
|
||||
axisTicks: {
|
||||
show: false,
|
||||
},
|
||||
tooltip: {
|
||||
enabled: true,
|
||||
},
|
||||
labels: {
|
||||
format: `MM/yy`,
|
||||
style: {
|
||||
colors: 'var(--color-font)',
|
||||
},
|
||||
},
|
||||
},
|
||||
yaxis: {
|
||||
labels: {
|
||||
style: {
|
||||
colors: 'var(--color-font)',
|
||||
},
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
x: {
|
||||
format: `MMM'yy`,
|
||||
},
|
||||
},
|
||||
title: {
|
||||
text: props.title,
|
||||
style: {
|
||||
color: 'var(--color-font)',
|
||||
},
|
||||
},
|
||||
responsive: [
|
||||
{
|
||||
breakpoint: 1920,
|
||||
options: {
|
||||
plotOptions: {
|
||||
bar: {
|
||||
columnWidth: '80%',
|
||||
},
|
||||
},
|
||||
dataLabels: {
|
||||
offsetY: -15,
|
||||
style: {
|
||||
fontSize: '9px',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
breakpoint: 768,
|
||||
options: {
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
const getAreaChartConfig = (): ApexCharts.ApexOptions => {
|
||||
return {
|
||||
chart: {
|
||||
id: `${props.id}BrushChart`,
|
||||
height: 120,
|
||||
type: 'area',
|
||||
zoom: {
|
||||
enabled: false,
|
||||
},
|
||||
redrawOnWindowResize: false,
|
||||
brush: {
|
||||
target: `${props.id}BarChart`,
|
||||
enabled: true,
|
||||
autoScaleYaxis: false,
|
||||
},
|
||||
selection: {
|
||||
enabled: true,
|
||||
xaxis: {
|
||||
min: moment().subtract(12, 'months').valueOf(), // We select last year
|
||||
max: moment.now(),
|
||||
},
|
||||
fill: {
|
||||
color: props.activeTheme === 'dark' ? '#222529' : secondaryColor,
|
||||
opacity: 0.2,
|
||||
},
|
||||
stroke: {
|
||||
width: 1,
|
||||
dashArray: 4,
|
||||
color: props.activeTheme === 'dark' ? '#222529' : secondaryColor,
|
||||
opacity: 1,
|
||||
},
|
||||
},
|
||||
fontFamily: "'Lato', Roboto, 'Helvetica Neue', Arial, sans-serif !default",
|
||||
toolbar: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
grid: { borderColor: 'var(--border-md)' },
|
||||
plotOptions: {
|
||||
bar: {
|
||||
borderRadius: 5,
|
||||
dataLabels: {
|
||||
position: 'top',
|
||||
},
|
||||
},
|
||||
},
|
||||
colors: ['var(--color-1-500)'],
|
||||
xaxis: {
|
||||
type: 'datetime',
|
||||
position: 'bottom',
|
||||
axisBorder: {
|
||||
show: false,
|
||||
},
|
||||
axisTicks: {
|
||||
show: false,
|
||||
},
|
||||
tooltip: {
|
||||
enabled: true,
|
||||
},
|
||||
labels: {
|
||||
format: `MM/yy`,
|
||||
style: {
|
||||
colors: 'var(--color-font)',
|
||||
},
|
||||
},
|
||||
},
|
||||
yaxis: {
|
||||
labels: {
|
||||
style: {
|
||||
colors: 'var(--color-font)',
|
||||
},
|
||||
},
|
||||
},
|
||||
stroke: {
|
||||
curve: 'smooth',
|
||||
},
|
||||
fill: {
|
||||
opacity: 0.5,
|
||||
colors: [
|
||||
() => {
|
||||
return props.activeTheme === 'dark' ? '#222529' : primaryColor;
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ReactApexChart
|
||||
options={getBarChartConfig()}
|
||||
series={[
|
||||
{
|
||||
name: 'Packages',
|
||||
data: props.series,
|
||||
},
|
||||
]}
|
||||
type="bar"
|
||||
height={300}
|
||||
/>
|
||||
{/* Brush chart is only visible when we have collected data from more than one year */}
|
||||
{props.series.length > 12 && (
|
||||
<ReactApexChart options={getAreaChartConfig()} series={[{ data: props.series }]} type="area" height={130} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default BrushChart;
|
||||
|
|
@ -5,24 +5,6 @@ exports[`StatsView creates snapshot 1`] = `
|
|||
<div
|
||||
class="d-flex flex-column flex-grow-1 position-relative"
|
||||
>
|
||||
<div
|
||||
class="position-absolute p-5 wrapper undefined"
|
||||
>
|
||||
<div
|
||||
class="d-flex flex-row align-items-center justify-content-center w-100 h-100"
|
||||
>
|
||||
<div
|
||||
class="spinner-border text-primary spinner undefined"
|
||||
role="status"
|
||||
>
|
||||
<span
|
||||
class="sr-only"
|
||||
>
|
||||
Loading...
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<main
|
||||
class="container-lg px-sm-4 px-lg-0 py-5 noFocus"
|
||||
id="content"
|
||||
|
|
@ -37,6 +19,295 @@ exports[`StatsView creates snapshot 1`] = `
|
|||
>
|
||||
Stats
|
||||
</div>
|
||||
<div
|
||||
class="text-center mb-5"
|
||||
>
|
||||
<small>
|
||||
<span
|
||||
class="text-muted mr-2"
|
||||
>
|
||||
Report generated at:
|
||||
</span>
|
||||
</small>
|
||||
</div>
|
||||
<span
|
||||
class="header"
|
||||
>
|
||||
<h2
|
||||
class="position-relative anchorHeader headingWrapper mb-4 font-weight-bold title"
|
||||
>
|
||||
<div
|
||||
class="position-absolute headerAnchor"
|
||||
data-testid="anchor"
|
||||
id="packages-and-releases"
|
||||
/>
|
||||
<a
|
||||
aria-label="Packages and releases"
|
||||
class="text-reset text-center d-none d-md-block headingLink"
|
||||
href="/#packages-and-releases"
|
||||
role="button"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
stroke="currentColor"
|
||||
stroke-width="0"
|
||||
viewBox="0 0 16 16"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
Packages and releases
|
||||
</h2>
|
||||
</span>
|
||||
<div
|
||||
class="row my-4 pb-0 pb-lg-4"
|
||||
>
|
||||
<div
|
||||
class="col-12 col-lg-6"
|
||||
>
|
||||
<div
|
||||
class="pr-0 pr-lg-3 pr-xxl-4 mt-4 mb-4 mb-lg-0"
|
||||
>
|
||||
<div
|
||||
class="card chartWrapper"
|
||||
>
|
||||
<div
|
||||
class="position-absolute p-5 wrapper undefined"
|
||||
>
|
||||
<div
|
||||
class="d-flex flex-row align-items-center justify-content-center w-100 h-100"
|
||||
>
|
||||
<div
|
||||
class="spinner-border text-primary spinner undefined"
|
||||
role="status"
|
||||
>
|
||||
<span
|
||||
class="sr-only"
|
||||
>
|
||||
Loading...
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
Chart
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="col-12 col-lg-6"
|
||||
>
|
||||
<div
|
||||
class="pl-0 pl-lg-3 pl-xxl-4 mt-4"
|
||||
>
|
||||
<div
|
||||
class="card chartWrapper"
|
||||
>
|
||||
<div
|
||||
class="position-absolute p-5 wrapper undefined"
|
||||
>
|
||||
<div
|
||||
class="d-flex flex-row align-items-center justify-content-center w-100 h-100"
|
||||
>
|
||||
<div
|
||||
class="spinner-border text-primary spinner undefined"
|
||||
role="status"
|
||||
>
|
||||
<span
|
||||
class="sr-only"
|
||||
>
|
||||
Loading...
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
Chart
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
class="header"
|
||||
>
|
||||
<h2
|
||||
class="position-relative anchorHeader headingWrapper my-4 font-weight-bold title"
|
||||
>
|
||||
<div
|
||||
class="position-absolute headerAnchor"
|
||||
data-testid="anchor"
|
||||
id="repositories"
|
||||
/>
|
||||
<a
|
||||
aria-label="Repositories"
|
||||
class="text-reset text-center d-none d-md-block headingLink"
|
||||
href="/#repositories"
|
||||
role="button"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
stroke="currentColor"
|
||||
stroke-width="0"
|
||||
viewBox="0 0 16 16"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
Repositories
|
||||
</h2>
|
||||
</span>
|
||||
<div
|
||||
class="row my-4"
|
||||
>
|
||||
<div
|
||||
class="col-12 my-4"
|
||||
>
|
||||
<div
|
||||
class="card chartWrapper"
|
||||
>
|
||||
<div
|
||||
class="position-absolute p-5 wrapper undefined"
|
||||
>
|
||||
<div
|
||||
class="d-flex flex-row align-items-center justify-content-center w-100 h-100"
|
||||
>
|
||||
<div
|
||||
class="spinner-border text-primary spinner undefined"
|
||||
role="status"
|
||||
>
|
||||
<span
|
||||
class="sr-only"
|
||||
>
|
||||
Loading...
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
Chart
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
class="header"
|
||||
>
|
||||
<h2
|
||||
class="position-relative anchorHeader headingWrapper mt-4 font-weight-bold title"
|
||||
>
|
||||
<div
|
||||
class="position-absolute headerAnchor"
|
||||
data-testid="anchor"
|
||||
id="organizations-and-users"
|
||||
/>
|
||||
<a
|
||||
aria-label="Organizations and users"
|
||||
class="text-reset text-center d-none d-md-block headingLink"
|
||||
href="/#organizations-and-users"
|
||||
role="button"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
stroke="currentColor"
|
||||
stroke-width="0"
|
||||
viewBox="0 0 16 16"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
Organizations and users
|
||||
</h2>
|
||||
</span>
|
||||
<div
|
||||
class="row my-4"
|
||||
>
|
||||
<div
|
||||
class="col-12 col-lg-6"
|
||||
>
|
||||
<div
|
||||
class="pr-0 pr-lg-3 pr-xxl-4 pt-4"
|
||||
>
|
||||
<div
|
||||
class="card chartWrapper"
|
||||
>
|
||||
<div
|
||||
class="position-absolute p-5 wrapper undefined"
|
||||
>
|
||||
<div
|
||||
class="d-flex flex-row align-items-center justify-content-center w-100 h-100"
|
||||
>
|
||||
<div
|
||||
class="spinner-border text-primary spinner undefined"
|
||||
role="status"
|
||||
>
|
||||
<span
|
||||
class="sr-only"
|
||||
>
|
||||
Loading...
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
Chart
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="col-12 col-lg-6"
|
||||
>
|
||||
<div
|
||||
class="pl-0 pl-lg-3 pl-xxl-4 pt-4"
|
||||
>
|
||||
<div
|
||||
class="card chartWrapper"
|
||||
>
|
||||
<div
|
||||
class="position-absolute p-5 wrapper undefined"
|
||||
>
|
||||
<div
|
||||
class="d-flex flex-row align-items-center justify-content-center w-100 h-100"
|
||||
>
|
||||
<div
|
||||
class="spinner-border text-primary spinner undefined"
|
||||
role="status"
|
||||
>
|
||||
<span
|
||||
class="sr-only"
|
||||
>
|
||||
Loading...
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
Chart
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { AppCtx } from '../../context/AppCtx';
|
|||
import { AHStats, ErrorKind } from '../../types';
|
||||
import StatsView from './index';
|
||||
jest.mock('../../api');
|
||||
jest.mock('./BrushChart', () => () => <div>Chart</div>);
|
||||
jest.mock('react-apexcharts', () => () => <div>Chart</div>);
|
||||
|
||||
const getMockStats = (fixtureId: string): AHStats => {
|
||||
|
|
|
|||
|
|
@ -11,10 +11,10 @@ import { AHStats } from '../../types';
|
|||
import compoundErrorMessage from '../../utils/compoundErrorMessage';
|
||||
import getMetaTag from '../../utils/getMetaTag';
|
||||
import isWhiteLabel from '../../utils/isWhiteLabel';
|
||||
import prettifyNumber from '../../utils/prettifyNumber';
|
||||
import AnchorHeader from '../common/AnchorHeader';
|
||||
import Loading from '../common/Loading';
|
||||
import NoData from '../common/NoData';
|
||||
import BrushChart from './BrushChart';
|
||||
import styles from './StatsView.module.css';
|
||||
|
||||
interface Props {
|
||||
|
|
@ -30,7 +30,28 @@ const StatsView = (props: Props) => {
|
|||
const { effective } = ctx.prefs.theme;
|
||||
const [activeTheme, setActiveTheme] = useState(effective);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [stats, setStats] = useState<AHStats | null | undefined>(undefined);
|
||||
const [stats, setStats] = useState<AHStats | null>({
|
||||
packages: {
|
||||
total: 0,
|
||||
runningTotal: [],
|
||||
},
|
||||
snapshots: {
|
||||
total: 0,
|
||||
runningTotal: [],
|
||||
},
|
||||
repositories: {
|
||||
total: 0,
|
||||
runningTotal: [],
|
||||
},
|
||||
organizations: {
|
||||
total: 0,
|
||||
runningTotal: [],
|
||||
},
|
||||
users: {
|
||||
total: 0,
|
||||
runningTotal: [],
|
||||
},
|
||||
});
|
||||
const [apiError, setApiError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -142,7 +163,6 @@ const StatsView = (props: Props) => {
|
|||
},
|
||||
xaxis: {
|
||||
type: 'datetime',
|
||||
tickPlacement: 'on',
|
||||
labels: {
|
||||
datetimeFormatter: {
|
||||
year: 'yyyy',
|
||||
|
|
@ -169,103 +189,6 @@ const StatsView = (props: Props) => {
|
|||
};
|
||||
};
|
||||
|
||||
const getBarChartConfig = (title: string): ApexCharts.ApexOptions => {
|
||||
return {
|
||||
chart: {
|
||||
height: 300,
|
||||
type: 'bar',
|
||||
fontFamily: "'Lato', Roboto, 'Helvetica Neue', Arial, sans-serif !default",
|
||||
toolbar: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
grid: { borderColor: 'var(--border-md)' },
|
||||
plotOptions: {
|
||||
bar: {
|
||||
borderRadius: 5,
|
||||
dataLabels: {
|
||||
position: 'top',
|
||||
},
|
||||
},
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: true,
|
||||
offsetY: -20,
|
||||
style: {
|
||||
fontSize: '12px',
|
||||
colors: ['var(--color-font)'],
|
||||
},
|
||||
formatter: (value: number) => {
|
||||
return prettifyNumber(value);
|
||||
},
|
||||
},
|
||||
colors: ['var(--color-1-500)'],
|
||||
xaxis: {
|
||||
type: 'datetime',
|
||||
position: 'bottom',
|
||||
axisBorder: {
|
||||
show: false,
|
||||
},
|
||||
axisTicks: {
|
||||
show: false,
|
||||
},
|
||||
tooltip: {
|
||||
enabled: true,
|
||||
},
|
||||
labels: {
|
||||
format: `MM/yy`,
|
||||
style: {
|
||||
colors: 'var(--color-font)',
|
||||
},
|
||||
},
|
||||
},
|
||||
yaxis: {
|
||||
labels: {
|
||||
style: {
|
||||
colors: 'var(--color-font)',
|
||||
},
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
x: {
|
||||
format: `MMM'yy`,
|
||||
},
|
||||
},
|
||||
title: {
|
||||
text: title,
|
||||
style: {
|
||||
color: 'var(--color-font)',
|
||||
},
|
||||
},
|
||||
responsive: [
|
||||
{
|
||||
breakpoint: 1920,
|
||||
options: {
|
||||
plotOptions: {
|
||||
bar: {
|
||||
columnWidth: '80%',
|
||||
},
|
||||
},
|
||||
dataLabels: {
|
||||
offsetY: -15,
|
||||
style: {
|
||||
fontSize: '9px',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
breakpoint: 768,
|
||||
options: {
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
async function getStats() {
|
||||
try {
|
||||
|
|
@ -309,8 +232,6 @@ const StatsView = (props: Props) => {
|
|||
|
||||
return (
|
||||
<div className="d-flex flex-column flex-grow-1 position-relative">
|
||||
{(isUndefined(stats) || isLoading) && <Loading />}
|
||||
|
||||
<main role="main" className="container-lg px-sm-4 px-lg-0 py-5 noFocus" id="content" tabIndex={-1}>
|
||||
<div className="flex-grow-1 position-relative">
|
||||
<div className={`h2 text-dark text-center ${styles.title}`}>{siteName} Stats</div>
|
||||
|
|
@ -322,7 +243,7 @@ const StatsView = (props: Props) => {
|
|||
<div className="text-center mb-5">
|
||||
<small>
|
||||
<span className="text-muted mr-2">Report generated at:</span>
|
||||
{moment(stats.generatedAt).format('YYYY/MM/DD HH:mm:ss (Z)')}
|
||||
{stats.generatedAt ? moment(stats.generatedAt).format('YYYY/MM/DD HH:mm:ss (Z)') : ''}
|
||||
</small>
|
||||
</div>
|
||||
|
||||
|
|
@ -344,6 +265,7 @@ const StatsView = (props: Props) => {
|
|||
<div className={classnames('col-12', { 'col-lg-6': stats.snapshots.runningTotal })}>
|
||||
<div className="pr-0 pr-lg-3 pr-xxl-4 mt-4 mb-4 mb-lg-0">
|
||||
<div className={`card ${styles.chartWrapper}`}>
|
||||
{(stats.snapshots.runningTotal!.length === 0 || isLoading) && <Loading />}
|
||||
<ReactApexChart
|
||||
options={getAreaChartConfig('Packages available')}
|
||||
series={[{ name: 'Packages', data: stats.packages.runningTotal }]}
|
||||
|
|
@ -359,6 +281,7 @@ const StatsView = (props: Props) => {
|
|||
<div className={classnames('col-12', { 'col-lg-6': stats.packages.runningTotal })}>
|
||||
<div className="pl-0 pl-lg-3 pl-xxl-4 mt-4">
|
||||
<div className={`card ${styles.chartWrapper}`}>
|
||||
{(stats.packages.runningTotal!.length === 0 || isLoading) && <Loading />}
|
||||
<ReactApexChart
|
||||
options={getAreaChartConfig('Releases available')}
|
||||
series={[{ name: 'Releases', data: stats.snapshots.runningTotal }]}
|
||||
|
|
@ -378,16 +301,12 @@ const StatsView = (props: Props) => {
|
|||
<div className={classnames('col-12', { 'col-lg-6': stats.snapshots.createdMonthly })}>
|
||||
<div className="pr-0 pr-lg-3 pr-xxl-4 mt-4 mb-4 mb-lg-0">
|
||||
<div className={`card ${styles.chartWrapper}`}>
|
||||
<ReactApexChart
|
||||
options={getBarChartConfig('New packages added monthly')}
|
||||
series={[
|
||||
{
|
||||
name: 'Packages',
|
||||
data: stats.packages.createdMonthly,
|
||||
},
|
||||
]}
|
||||
type="bar"
|
||||
height={300}
|
||||
{(stats.packages.createdMonthly!.length === 0 || isLoading) && <Loading />}
|
||||
<BrushChart
|
||||
series={stats.packages.createdMonthly}
|
||||
title="New packages added monthly"
|
||||
id="snapshots"
|
||||
activeTheme={activeTheme}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -398,16 +317,12 @@ const StatsView = (props: Props) => {
|
|||
<div className={classnames('col-12', { 'col-lg-6': stats.packages.createdMonthly })}>
|
||||
<div className="pl-0 pl-lg-3 pl-xxl-4 mt-4">
|
||||
<div className={`card ${styles.chartWrapper}`}>
|
||||
<ReactApexChart
|
||||
options={getBarChartConfig('New releases added monthly')}
|
||||
series={[
|
||||
{
|
||||
name: 'Releases',
|
||||
data: stats.snapshots.createdMonthly,
|
||||
},
|
||||
]}
|
||||
type="bar"
|
||||
height={300}
|
||||
{(stats.snapshots.createdMonthly!.length === 0 || isLoading) && <Loading />}
|
||||
<BrushChart
|
||||
series={stats.snapshots.createdMonthly}
|
||||
title="New packages added monthly"
|
||||
id="packages"
|
||||
activeTheme={activeTheme}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -430,6 +345,7 @@ const StatsView = (props: Props) => {
|
|||
<div className="row my-4">
|
||||
<div className="col-12 my-4">
|
||||
<div className={`card ${styles.chartWrapper}`}>
|
||||
{(stats.repositories.runningTotal!.length === 0 || isLoading) && <Loading />}
|
||||
<ReactApexChart
|
||||
options={getAreaChartConfig('Registered repositories')}
|
||||
series={[{ name: 'Repositories', data: stats.repositories.runningTotal }]}
|
||||
|
|
@ -455,6 +371,7 @@ const StatsView = (props: Props) => {
|
|||
<div className={classnames('col-12', { 'col-lg-6': stats.users.runningTotal })}>
|
||||
<div className="pr-0 pr-lg-3 pr-xxl-4 pt-4">
|
||||
<div className={`card ${styles.chartWrapper}`}>
|
||||
{(stats.organizations.runningTotal!.length === 0 || isLoading) && <Loading />}
|
||||
<ReactApexChart
|
||||
options={getAreaChartConfig('Registered organizations', true)}
|
||||
series={[
|
||||
|
|
@ -475,6 +392,7 @@ const StatsView = (props: Props) => {
|
|||
<div className={classnames('col-12', { 'col-lg-6': stats.organizations.runningTotal })}>
|
||||
<div className="pl-0 pl-lg-3 pl-xxl-4 pt-4">
|
||||
<div className={`card ${styles.chartWrapper}`}>
|
||||
{(stats.users.runningTotal!.length === 0 || isLoading) && <Loading />}
|
||||
<ReactApexChart
|
||||
options={getAreaChartConfig('Registered users', true)}
|
||||
series={[
|
||||
|
|
|
|||
|
|
@ -646,7 +646,7 @@ export enum ChartTemplateSpecialType {
|
|||
}
|
||||
|
||||
export interface AHStats {
|
||||
generatedAt: number;
|
||||
generatedAt?: number;
|
||||
packages: {
|
||||
total: number;
|
||||
runningTotal?: any[];
|
||||
|
|
|
|||
|
|
@ -2421,7 +2421,7 @@ anymatch@~3.1.2:
|
|||
normalize-path "^3.0.0"
|
||||
picomatch "^2.0.4"
|
||||
|
||||
apexcharts@^3.26.0:
|
||||
apexcharts@^3.27.3:
|
||||
version "3.28.1"
|
||||
resolved "https://registry.yarnpkg.com/apexcharts/-/apexcharts-3.28.1.tgz#35a17ea9ef9a1a93fb01ce79d245af8aedb59a7b"
|
||||
integrity sha512-5M1KitI/XmY2Sx6ih9vQOXyQUTmotDG/cML2N6bkBlVseF10RPSzM7dkrf7Y68apSZF6e7J581gXXu1+qkLhCA==
|
||||
|
|
@ -10215,7 +10215,7 @@ raw-body@2.4.0:
|
|||
iconv-lite "0.4.24"
|
||||
unpipe "1.0.0"
|
||||
|
||||
react-apexcharts@^1.3.7:
|
||||
react-apexcharts@^1.3.9:
|
||||
version "1.3.9"
|
||||
resolved "https://registry.yarnpkg.com/react-apexcharts/-/react-apexcharts-1.3.9.tgz#d97e53fd513dc6ff73b90c2364c3bdd88d8dad01"
|
||||
integrity sha512-KPonT5uQPHOHSVgTNEzpB0HhCkZtoicQYGjR9P+3DRDSgTsC+DM2vDUfo/B2Fn1m+wdgVeDXWL0VJYDc6JD/tw==
|
||||
|
|
|
|||
Loading…
Reference in New Issue