Skip to content

Commit 864b07e

Browse files
authored
Merge pull request #5829 from bcgov/feat/5736
feat(5736): replace barchart implementation with Chart.js
2 parents 4d0fc13 + e53fda0 commit 864b07e

File tree

11 files changed

+111
-87
lines changed

11 files changed

+111
-87
lines changed

app/app/analytics/general/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,14 @@ export default analyticsDashboard(() => {
4545
</div>
4646
<div className="flex flex-col gap-y-12 mt-14">
4747
<CombinedAreaGraph
48+
index="date"
4849
isLoading={isLoading}
4950
title="Daily User Login Events"
5051
subtitle={`This chart displays the number of login events per day from ${formatDate(
5152
startDate,
5253
)} to ${formatDate(endDate)}.`}
5354
chartData={data}
5455
categories={['Logins']}
55-
colors={['indigo']}
5656
onExport={() => downloadAnalyticsGeneral({ dates: snap.dates, userId: snap.userId })}
5757
/>
5858
</div>

app/app/analytics/private-cloud/ActiveProducts.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useSnapshot } from 'valtio';
2-
import LineGraph from '@/components/analytics/LineGraph';
2+
import CombinedAreaGraph from '@/components/analytics/CombinedAreaGraph';
33
import { clusters } from '@/constants';
44
import { downloadPrivateCloudActiveProducts } from '@/services/backend/analytics/private-cloud';
55
import { ActiveProduct } from '@/types/analytics-private';
@@ -11,7 +11,7 @@ export default function ActiveProducts({ data }: { data: ActiveProduct[] }) {
1111
const startDate = pageSnapshot.dates?.[0] ?? data?.[0]?.date;
1212
const endDate = pageSnapshot.dates?.[1] ?? new Date();
1313
return (
14-
<LineGraph
14+
<CombinedAreaGraph
1515
index="date"
1616
title="Active Products"
1717
subtitle={`This chart displays the cumulative number of products created from ${formatDate(

app/app/analytics/private-cloud/AllRequests.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ export default function AllRequests({ data }: { data: AllRequests[] }) {
1111
const endDate = pageSnapshot.dates?.[1] ?? new Date();
1212
return (
1313
<CombinedAreaGraph
14+
index="date"
1415
title="Requests over time"
1516
subtitle={`This chart displays the number of requests created over time for each request type from ${formatDate(
1617
startDate,
1718
)} to ${formatDate(endDate)}.`}
1819
chartData={data}
1920
categories={['All requests', 'Edit requests', 'Create requests', 'Delete requests']}
20-
colors={['indigo', 'yellow', 'green', 'red']}
2121
onExport={() => downloadPrivateCloudAllRequests({ data: { ...pageSnapshot } })}
2222
/>
2323
);

app/app/analytics/private-cloud/ContactChangeRequests.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ export default function ContactChangeRequests({ data }: { data: ContactsChange[]
1111
const endDate = pageSnapshot.dates?.[1] ?? new Date();
1212
return (
1313
<CombinedAreaGraph
14+
index="date"
1415
title="Contact change requests over time"
1516
subtitle={`This chart displays edit requests where contact changes were requested and the request decision from ${formatDate(
1617
startDate,
1718
)} to ${formatDate(endDate)}.`}
1819
chartData={data}
1920
categories={['Contact changes']}
20-
colors={['indigo']}
2121
onExport={() => downloadPrivateCloudContactChangeRequests({ data: { ...pageSnapshot } })}
2222
/>
2323
);

app/app/analytics/private-cloud/QuotaRequests.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ export default function QuotaRequests({ data }: { data: QuotaChange[] }) {
1111
const endDate = pageSnapshot.dates?.[1] ?? new Date();
1212
return (
1313
<CombinedAreaGraph
14+
index="date"
1415
title="Quota requests over time"
1516
subtitle={`This chart displays edit requests where a quota change was requested and the request decision from ${formatDate(
1617
startDate,
1718
)} to ${formatDate(endDate)}.`}
1819
chartData={data}
1920
categories={['All quota requests', 'Approved quota requests', 'Rejected quota requests']}
20-
colors={['indigo', 'green', 'red']}
2121
onExport={() => downloadPrivateCloudQuotaChangeRequests({ data: { ...pageSnapshot } })}
2222
/>
2323
);

app/app/analytics/public-cloud/ActiveProducts.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useSnapshot } from 'valtio';
2-
import LineGraph from '@/components/analytics/LineGraph';
2+
import CombinedAreaGraph from '@/components/analytics/CombinedAreaGraph';
33
import { providers } from '@/constants';
44
import { downloadPublicCloudActiveProducts } from '@/services/backend/analytics/public-cloud';
55
import { ActiveProduct } from '@/types/analytics-public';
@@ -11,7 +11,7 @@ export default function ActiveProducts({ data }: { data: ActiveProduct[] }) {
1111
const startDate = pageSnapshot.dates?.[0] ?? data?.[0]?.date;
1212
const endDate = pageSnapshot.dates?.[1] ?? new Date();
1313
return (
14-
<LineGraph
14+
<CombinedAreaGraph
1515
index="date"
1616
title="Active Products"
1717
subtitle={`This chart displays the cumulative number of products created from ${formatDate(

app/app/analytics/public-cloud/AllRequests.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ export default function AllRequests({ data }: { data: AllRequests[] }) {
1111
const endDate = pageSnapshot.dates?.[1] ?? new Date();
1212
return (
1313
<CombinedAreaGraph
14+
index="date"
1415
title="Requests over time"
1516
subtitle={`This chart displays the number of requests created over time for each request type from ${formatDate(
1617
startDate,
1718
)} to ${formatDate(endDate)}.`}
1819
chartData={data}
1920
categories={['All requests', 'Edit requests', 'Create requests', 'Delete requests']}
20-
colors={['indigo', 'yellow', 'green', 'red']}
2121
onExport={async () => downloadPublicCloudAllRequests({ data: { ...pageSnapshot } })}
2222
/>
2323
);

app/app/analytics/public-cloud/ContactChangeRequests.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ export default function ContactChangeRequests({ data }: { data: ContactsChange[]
1111
const endDate = pageSnapshot.dates?.[1] ?? new Date();
1212
return (
1313
<CombinedAreaGraph
14+
index="date"
1415
title="Contact change requests over time"
1516
subtitle={`This chart displays edit requests where contact changes were requested and the request decision from ${formatDate(
1617
startDate,
1718
)} to ${formatDate(endDate)}.`}
1819
chartData={data}
1920
categories={['Contact changes']}
20-
colors={['indigo']}
2121
onExport={async () => downloadPublicCloudContactChangeRequests({ data: { ...pageSnapshot } })}
2222
/>
2323
);

app/components/analytics/CombinedAreaGraph.tsx

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,21 @@ export type ChartDate = {
1111
};
1212

1313
export default function CombinedAreaGraph({
14+
index,
1415
title,
1516
subtitle,
1617
onExport,
1718
chartData,
1819
categories,
19-
colors,
2020
isLoading = false,
2121
exportApiEndpoint /* temporary */,
2222
}: {
23+
index: string;
2324
title: string;
2425
subtitle: string;
2526
onExport?: () => Promise<boolean>;
2627
chartData: any;
2728
categories: string[];
28-
colors: string[];
2929
isLoading?: boolean;
3030
exportApiEndpoint?: string /* temporary */;
3131
}) {
@@ -37,7 +37,7 @@ export default function CombinedAreaGraph({
3737

3838
for (const row of chartData) {
3939
for (const [key, value] of Object.entries(row)) {
40-
if (key === 'date') {
40+
if (key === index) {
4141
labels.push(String(value));
4242
continue;
4343
}
@@ -86,6 +86,21 @@ export default function CombinedAreaGraph({
8686
},
8787
},
8888
},
89+
scales: {
90+
x: {
91+
ticks: {
92+
autoSkip: true,
93+
maxRotation: 0,
94+
minRotation: 0,
95+
},
96+
},
97+
y: {
98+
ticks: {
99+
callback: (value: string | number) => value,
100+
},
101+
beginAtZero: true,
102+
},
103+
},
89104
};
90105

91106
return { data: _data, options: _options };

app/components/analytics/Histogram.tsx

Lines changed: 83 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
import { BarChart, Card, Subtitle, Title } from '@tremor/react';
1+
import { Card, Subtitle, Title } from '@tremor/react';
2+
import { ChartTypeRegistry, TooltipItem } from 'chart.js';
3+
import { useMemo } from 'react';
4+
import { Bar } from 'react-chartjs-2';
5+
import { valueFormatter, getColor } from '@/components/analytics/helpers';
26
import ExportButton from '@/components/buttons/ExportButton';
37

4-
const valueFormatter = (number: number) => `${new Intl.NumberFormat('us').format(number).toString()}%`;
5-
68
export default function Histogram({
79
index,
810
onExport,
@@ -22,22 +24,91 @@ export default function Histogram({
2224
categories: string[];
2325
colors: string[];
2426
}) {
27+
const { data, options } = useMemo(() => {
28+
if (!chartData) return { data: { labels: [], datasets: [] }, options: {} };
29+
30+
const labels: string[] = [];
31+
const datasetMap: Record<string, number[]> = {};
32+
33+
for (const row of chartData) {
34+
for (const [key, value] of Object.entries(row)) {
35+
if (key === 'time') {
36+
labels.push(String(value));
37+
continue;
38+
}
39+
40+
if (!datasetMap[key]) {
41+
datasetMap[key] = [];
42+
}
43+
44+
datasetMap[key].push(Number(value));
45+
}
46+
}
47+
48+
const _data = {
49+
labels,
50+
datasets: Object.entries(datasetMap).map(([label, data], index) => ({
51+
label,
52+
data,
53+
backgroundColor: getColor(index),
54+
borderRadius: 4,
55+
barThickness: 'flex' as const,
56+
})),
57+
};
58+
59+
const _options = {
60+
responsive: true,
61+
maintainAspectRatio: true,
62+
interaction: {
63+
mode: 'index' as const,
64+
intersect: false,
65+
},
66+
plugins: {
67+
legend: {
68+
position: 'top' as const,
69+
},
70+
title: {
71+
display: false,
72+
text: '',
73+
},
74+
tooltip: {
75+
callbacks: {
76+
label: function (context: TooltipItem<keyof ChartTypeRegistry>) {
77+
const label = context.dataset.label || 'Unknown';
78+
const value = valueFormatter(Number(context.raw as number));
79+
return `${label}: ${value}`;
80+
},
81+
},
82+
},
83+
},
84+
scales: {
85+
x: {
86+
ticks: {
87+
autoSkip: true,
88+
maxRotation: 0,
89+
minRotation: 0,
90+
},
91+
},
92+
y: {
93+
ticks: {
94+
callback: (value: string | number) => `${value}%`,
95+
},
96+
beginAtZero: true,
97+
},
98+
},
99+
};
100+
101+
return { data: _data, options: _options };
102+
}, [chartData]);
103+
25104
return (
26105
<div className="flex flex-col items-end">
27106
<ExportButton className="mb-4" onExport={onExport} downloadUrl={exportApiEndpoint} />
28107
<Card>
29108
<Title>{title}</Title>
30109
<Subtitle>{subtitle}</Subtitle>
31110
<div className="relative">
32-
<BarChart
33-
className="mt-6"
34-
data={chartData}
35-
index={index}
36-
categories={categories}
37-
colors={colors}
38-
valueFormatter={valueFormatter}
39-
yAxisWidth={48}
40-
/>
111+
<Bar className="max-h-[28rem] mt-4" data={data} options={options} />
41112
</div>
42113
</Card>
43114
</div>

0 commit comments

Comments
 (0)