|
1 | | -import React, { useEffect, useState } from "react"; |
2 | | -import type { DashboardAnalyticsConfig } from "../../types"; |
3 | | -import { AxisOptions, Chart } from "react-charts"; |
| 1 | +import React, { useEffect, useState, lazy, useReducer, useRef } from "react"; |
| 2 | +import type { |
| 3 | + DashboardAnalyticsConfig, |
| 4 | + ChartDataPoint, |
| 5 | + ChartWidget, |
| 6 | +} from "../../types"; |
| 7 | +import type { AxisOptions } from "react-charts"; |
| 8 | +import { MetricMap } from "../../providers/plausible/client"; |
| 9 | +import { useTheme } from "payload/dist/admin/components/utilities/Theme"; |
4 | 10 |
|
5 | | -type Props = {}; |
| 11 | +type ChartData = { |
| 12 | + label: string; |
| 13 | + data: ChartDataPoint[]; |
| 14 | +}; |
| 15 | + |
| 16 | +type ChartOptions = { |
| 17 | + timeframe?: string; |
| 18 | + metric: ChartWidget["metric"]; |
| 19 | +}; |
6 | 20 |
|
7 | | -type ChartDataType = { |
8 | | - date: string; |
9 | | - pageviews: number; |
10 | | - visitors: number; |
| 21 | +type Props = { |
| 22 | + options: ChartOptions; |
11 | 23 | }; |
12 | 24 |
|
13 | | -export const ViewsChart: React.FC<Props> = (props) => { |
14 | | - const [chartData, setChartData] = useState<ChartDataType[]>([]); |
| 25 | +const ChartComponent = lazy(() => |
| 26 | + import("react-charts").then((module) => { |
| 27 | + return { default: module.Chart }; |
| 28 | + }) |
| 29 | +); |
| 30 | + |
| 31 | +const ViewsChart: React.FC<Props> = ({ options }) => { |
| 32 | + const [chartData, setChartData] = useState<ChartData[]>([]); |
| 33 | + const chartRef = useRef<any>(null); |
| 34 | + const theme = useTheme(); |
15 | 35 |
|
16 | | - /* useEffect(() => { |
17 | | - const getChartData = fetch(`/api/analytics/globalChartData`).then( |
18 | | - (response) => response.json() |
19 | | - ); |
| 36 | + const { timeframe, metric } = options; |
| 37 | + |
| 38 | + const timeframeIndicator = |
| 39 | + timeframe === "month" |
| 40 | + ? new Date().toLocaleString("default", { month: "long" }) |
| 41 | + : timeframe ?? "30d"; |
| 42 | + |
| 43 | + useEffect(() => { |
| 44 | + const getChartData = fetch(`/api/analytics/globalChartData`, { |
| 45 | + method: "post", |
| 46 | + credentials: "include", |
| 47 | + headers: { |
| 48 | + Accept: "application/json", |
| 49 | + "Content-Type": "application/json", |
| 50 | + }, |
| 51 | + body: JSON.stringify({ timeframe: timeframe, metric: metric }), |
| 52 | + }).then((response) => response.json()); |
20 | 53 |
|
21 | | - getChartData.then((data) => { |
22 | | - console.log("fetched data from backend:", data); |
23 | | - setChartData(data); |
| 54 | + getChartData.then((data: ChartDataPoint[]) => { |
| 55 | + const processedData: ChartData[] = [ |
| 56 | + { |
| 57 | + label: "Visitors", |
| 58 | + data: data, |
| 59 | + }, |
| 60 | + ]; |
| 61 | + setChartData(processedData); |
24 | 62 | }); |
25 | | - }, []); */ |
26 | 63 |
|
27 | | - /* const primaryAxis = React.useMemo<AxisOptions<ChartDataType>>( |
28 | | - () => ({ |
29 | | - getValue: (datum) => datum.date as unknown as Date, |
30 | | - }), |
31 | | - [] |
32 | | - ); |
| 64 | + const importChart = async () => { |
| 65 | + const { Chart } = await import("react-charts"); |
| 66 | + chartRef.current = Chart; |
| 67 | + }; |
| 68 | + |
| 69 | + importChart(); |
| 70 | + }, []); |
| 71 | + |
| 72 | + const primaryAxis = React.useMemo<AxisOptions<ChartDataPoint>>(() => { |
| 73 | + return { |
| 74 | + getValue: (datum) => datum.timestamp, |
| 75 | + show: false, |
| 76 | + elementType: "line", |
| 77 | + showDatumElements: false, |
| 78 | + }; |
| 79 | + }, []); |
33 | 80 |
|
34 | | - const secondaryAxes = React.useMemo<AxisOptions<ChartDataType>[]>( |
| 81 | + const secondaryAxes = React.useMemo<AxisOptions<ChartDataPoint>[]>( |
35 | 82 | () => [ |
36 | 83 | { |
37 | | - getValue: (datum) => datum.visitors, |
| 84 | + getValue: (datum) => { |
| 85 | + return datum.value; |
| 86 | + }, |
| 87 | + elementType: "line", |
38 | 88 | }, |
39 | 89 | ], |
40 | 90 | [] |
41 | | - ); */ |
| 91 | + ); |
42 | 92 |
|
43 | 93 | return ( |
44 | | - <div |
| 94 | + <section |
45 | 95 | style={{ |
46 | | - marginBottom: "20px", |
| 96 | + marginBottom: "1.5rem", |
47 | 97 | }} |
48 | 98 | > |
49 | | - my chart goes here: |
50 | | - {/* <Chart |
51 | | - options={{ |
52 | | - chartData, |
53 | | - primaryAxis, |
54 | | - secondaryAxes, |
55 | | - }} |
56 | | - /> */} |
57 | | - </div> |
| 99 | + {chartRef?.current && chartData?.length && chartData.length > 0 ? ( |
| 100 | + <> |
| 101 | + <h1 style={{ fontSize: "1.25rem", marginBottom: "0.5rem" }}> |
| 102 | + {MetricMap[metric].label} ({timeframeIndicator}) |
| 103 | + </h1> |
| 104 | + <div style={{ minHeight: "200px", position: "relative" }}> |
| 105 | + <ChartComponent |
| 106 | + options={{ |
| 107 | + data: chartData, |
| 108 | + dark: theme.theme === "dark", |
| 109 | + initialHeight: 220, |
| 110 | + tooltip: false, |
| 111 | + /* @ts-ignore */ |
| 112 | + primaryAxis, |
| 113 | + /* @ts-ignore */ |
| 114 | + secondaryAxes, |
| 115 | + }} |
| 116 | + /> |
| 117 | + </div> |
| 118 | + </> |
| 119 | + ) : ( |
| 120 | + <></> |
| 121 | + )} |
| 122 | + </section> |
58 | 123 | ); |
59 | 124 | }; |
60 | 125 |
|
61 | | -export const getViewsChart = (props: any) => <ViewsChart {...props} />; |
| 126 | +export const getViewsChart = (props?: any, options?: ChartOptions) => { |
| 127 | + const combinedProps: Props = { |
| 128 | + ...props, |
| 129 | + options, |
| 130 | + }; |
| 131 | + return <ViewsChart {...combinedProps} />; |
| 132 | +}; |
| 133 | + |
| 134 | +export default { ViewsChart, getViewsChart }; |
0 commit comments