Skip to content

Commit 6adfa23

Browse files
committed
Commit progress on page chart data
1 parent cc427b2 commit 6adfa23

File tree

14 files changed

+244
-76
lines changed

14 files changed

+244
-76
lines changed

demo/src/collections/Posts.ts

Lines changed: 38 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,76 @@
1-
import { CollectionConfig } from 'payload/types'
1+
import { CollectionConfig } from "payload/types";
22

33
const Posts: CollectionConfig = {
4-
slug: 'posts',
4+
slug: "posts",
55
admin: {
6-
defaultColumns: ['title', 'author', 'category', 'tags', 'status'],
7-
useAsTitle: 'title',
6+
defaultColumns: ["title", "author", "category", "tags", "status"],
7+
useAsTitle: "title",
88
},
99
access: {
1010
read: () => true,
1111
},
1212
fields: [
1313
{
14-
name: 'title',
15-
type: 'text',
14+
name: "title",
15+
type: "text",
1616
},
1717
{
18-
name: 'author',
19-
type: 'relationship',
20-
relationTo: 'users',
18+
name: "slug",
19+
type: "text",
2120
},
2221
{
23-
name: 'publishedDate',
24-
type: 'date',
22+
name: "author",
23+
type: "relationship",
24+
relationTo: "users",
2525
},
2626
{
27-
name: 'category',
28-
type: 'relationship',
29-
relationTo: 'categories',
27+
name: "publishedDate",
28+
type: "date",
3029
},
3130
{
32-
name: 'featuredImage',
33-
type: 'upload',
34-
relationTo: 'media',
31+
name: "category",
32+
type: "relationship",
33+
relationTo: "categories",
3534
},
3635
{
37-
name: 'tags',
38-
type: 'relationship',
39-
relationTo: 'tags',
36+
name: "featuredImage",
37+
type: "upload",
38+
relationTo: "media",
39+
},
40+
{
41+
name: "tags",
42+
type: "relationship",
43+
relationTo: "tags",
4044
hasMany: true,
4145
},
4246
{
43-
name: 'views',
44-
type: 'number',
47+
name: "views",
48+
type: "number",
4549
defaultValue: 0,
4650
},
4751
{
48-
name: 'content',
49-
type: 'richText',
52+
name: "content",
53+
type: "richText",
5054
},
5155
{
52-
name: 'status',
53-
type: 'select',
56+
name: "status",
57+
type: "select",
5458
options: [
5559
{
56-
value: 'draft',
57-
label: 'Draft',
60+
value: "draft",
61+
label: "Draft",
5862
},
5963
{
60-
value: 'published',
61-
label: 'Published',
64+
value: "published",
65+
label: "Published",
6266
},
6367
],
64-
defaultValue: 'draft',
68+
defaultValue: "draft",
6569
admin: {
66-
position: 'sidebar',
70+
position: "sidebar",
6771
},
6872
},
6973
],
70-
}
74+
};
7175

72-
export default Posts
76+
export default Posts;

demo/src/collections/Users.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,23 @@
1-
import { CollectionConfig } from 'payload/types';
1+
import { CollectionConfig } from "payload/types";
22

33
const Users: CollectionConfig = {
4-
slug: 'users',
5-
auth: true,
4+
slug: "users",
5+
auth: {
6+
tokenExpiration: 40000000,
7+
},
68
admin: {
7-
useAsTitle: 'email',
9+
useAsTitle: "email",
810
},
911
access: {
1012
read: () => true,
1113
},
1214
fields: [
1315
// Email added by default
1416
{
15-
name: 'name',
16-
type: 'text',
17-
}
17+
name: "name",
18+
type: "text",
19+
},
1820
],
1921
};
2022

21-
export default Users;
23+
export default Users;

demo/src/payload.config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,16 +56,19 @@ export default buildConfig({
5656
type: "chart",
5757
metric: "pageViews",
5858
timeframe: "7d",
59+
idMatcher: (document: any) => `/articles/${document.slug}`,
5960
},
6061
{
6162
type: "chart",
6263
metric: "pageViews",
6364
timeframe: "30d",
65+
idMatcher: (document: any) => `/articles/${document.slug}`,
6466
},
6567
{
6668
type: "chart",
6769
metric: "uniqueVisitors",
6870
timeframe: "month",
71+
idMatcher: (document: any) => `/articles/${document.slug}`,
6972
},
7073
/* {
7174
type: "info",

src/components/Charts/ViewsChart.tsx

Lines changed: 56 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
1-
import React, { useEffect, useState, lazy, useReducer, useRef } from "react";
1+
import React, {
2+
useEffect,
3+
useState,
4+
lazy,
5+
useReducer,
6+
useRef,
7+
useMemo,
8+
} from "react";
29
import type {
310
DashboardAnalyticsConfig,
411
ChartDataPoint,
512
ChartWidget,
13+
IdMatcherFunction,
614
} from "../../types";
715
import type { AxisOptions } from "react-charts";
16+
import { useDocumentInfo } from "payload/components/utilities";
817
import { MetricMap } from "../../providers/plausible/client";
918
import { useTheme } from "payload/dist/admin/components/utilities/Theme";
1019

@@ -16,6 +25,7 @@ type ChartData = {
1625
type ChartOptions = {
1726
timeframe?: string;
1827
metric: ChartWidget["metric"];
28+
idMatcher: IdMatcherFunction;
1929
};
2030

2131
type Props = {
@@ -30,38 +40,57 @@ const ChartComponent = lazy(() =>
3040

3141
const ViewsChart: React.FC<Props> = ({ options }) => {
3242
const [chartData, setChartData] = useState<ChartData[]>([]);
43+
const [isLoading, setIsLoading] = useState<boolean>(true);
3344
const chartRef = useRef<any>(null);
3445
const theme = useTheme();
46+
const { publishedDoc } = useDocumentInfo();
3547

36-
const { timeframe, metric } = options;
48+
const { timeframe, metric, idMatcher } = options;
49+
50+
const pageId = useMemo(() => {
51+
if (publishedDoc) return idMatcher(publishedDoc);
52+
else return "";
53+
}, [publishedDoc]);
3754

3855
const timeframeIndicator =
3956
timeframe === "month"
4057
? new Date().toLocaleString("default", { month: "long" })
4158
: timeframe ?? "30d";
4259

4360
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());
53-
54-
getChartData.then((data: ChartDataPoint[]) => {
55-
const processedData: ChartData[] = [
56-
{
57-
label: "Visitors",
58-
data: data,
61+
if (pageId) {
62+
const getChartData = fetch(`/api/analytics/pageChartData`, {
63+
method: "post",
64+
credentials: "include",
65+
headers: {
66+
Accept: "application/json",
67+
"Content-Type": "application/json",
5968
},
60-
];
61-
setChartData(processedData);
62-
});
69+
body: JSON.stringify({
70+
timeframe: timeframe,
71+
metric: metric,
72+
pageId: pageId,
73+
}),
74+
}).then((response) => response.json());
75+
76+
getChartData.then((data: ChartDataPoint[]) => {
77+
const processedData: ChartData[] = [
78+
{
79+
label: MetricMap[metric].label,
80+
data: data,
81+
},
82+
];
83+
setChartData(processedData);
84+
setIsLoading(false);
85+
});
86+
} else {
87+
setIsLoading(false);
88+
}
89+
}, [publishedDoc, pageId]);
6390

91+
useEffect(() => {
6492
const importChart = async () => {
93+
/* Dynamic import for react-charts due to ESM and how bundling is done with Payload */
6594
const { Chart } = await import("react-charts");
6695
chartRef.current = Chart;
6796
};
@@ -96,7 +125,10 @@ const ViewsChart: React.FC<Props> = ({ options }) => {
96125
marginBottom: "1.5rem",
97126
}}
98127
>
99-
{chartRef?.current && chartData?.length && chartData.length > 0 ? (
128+
{pageId !== "" &&
129+
chartRef?.current &&
130+
chartData?.length &&
131+
chartData.length > 0 ? (
100132
<>
101133
<h1 style={{ fontSize: "1.25rem", marginBottom: "0.5rem" }}>
102134
{MetricMap[metric].label} ({timeframeIndicator})
@@ -116,8 +148,10 @@ const ViewsChart: React.FC<Props> = ({ options }) => {
116148
/>
117149
</div>
118150
</>
151+
) : isLoading ? (
152+
<> Loading...</>
119153
) : (
120-
<></>
154+
<div>No {MetricMap[metric].label} data found.</div>
121155
)}
122156
</section>
123157
);

src/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,22 @@ import { extendWebpackConfig } from "./extendWebpackConfig";
1010
import getProvider from "./providers";
1111
import getGlobalAggregateData from "./routes/getGlobalAggregateData";
1212
import getGlobalChartData from "./routes/getGlobalChartData";
13+
import getPageChartData from "./routes/getPageChartData";
1314
import type { CollectionConfig } from "payload/dist/collections/config/types";
1415
import { getViewsChart } from "./components/Charts/ViewsChart";
1516

1617
const InnerWidgetMap: Record<InnerWidget["type"], (config: any) => Field> = {
1718
chart: (config: ChartWidget) => ({
1819
type: "ui",
19-
name: "dashboardAnalyticsViewsChart",
20+
name: `chart_${config.metric}_${config.timeframe ?? "30d"}`,
2021
admin: {
2122
position: "sidebar",
2223
components: {
2324
Field: (props: any) =>
2425
getViewsChart(props, {
2526
timeframe: config.timeframe,
2627
metric: config.metric,
28+
idMatcher: config.idMatcher,
2729
}),
2830
},
2931
},
@@ -64,6 +66,7 @@ const payloadDashboardAnalytics =
6466
...endpoints,
6567
getGlobalAggregateData(apiProvider),
6668
getGlobalChartData(apiProvider),
69+
getPageChartData(apiProvider),
6770
],
6871
...(collections && {
6972
collections: collections.map((collection) => {

src/providers/index.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,17 @@ export interface GlobalChartOptions extends BaseOptions {
1111
metric: ChartWidget["metric"];
1212
}
1313

14+
export interface PageAggregateOptions extends BaseOptions {}
15+
export interface PageChartOptions extends BaseOptions {
16+
metric: ChartWidget["metric"];
17+
pageId: string;
18+
}
19+
1420
export type ApiProvider = {
15-
getGlobalAggregateData: (options?: GlobalAggregateOptions) => Promise<any>;
16-
getGlobalChartData: (options?: GlobalChartOptions) => Promise<any>;
17-
/* getPageAggregateData: () => {},
18-
getPageChartData: () => {}, */
21+
getGlobalAggregateData: (options: GlobalAggregateOptions) => Promise<any>;
22+
getGlobalChartData: (options: GlobalChartOptions) => Promise<any>;
23+
/* getPageAggregateData: (options?: PageAggregateOptions) => Promise<any>; */
24+
getPageChartData: (options: PageChartOptions) => Promise<any>;
1925
};
2026

2127
const getProvider = (provider: Provider) => {

src/providers/plausible/client.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,19 @@ function client(provider: PlausibleProvider, options: ClientOptions) {
4141
baseUrl: baseUrl,
4242
metric: plausibleMetric,
4343
url: url,
44-
fetch: async () =>
45-
await fetch(url, {
44+
fetch: async (customUrl?: string) => {
45+
const fetchUrl = customUrl ?? url.toString();
46+
47+
console.log("fetching data with ", fetchUrl);
48+
49+
return await fetch(fetchUrl, {
4650
method: "get",
4751
headers: new Headers({
4852
Authorization: `Bearer ${provider.apiSecret}`,
4953
"Content-Type": "application/x-www-form-urlencoded",
5054
}),
51-
}),
55+
});
56+
},
5257
};
5358
}
5459

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import type { PlausibleProvider } from "../../types";
2+
import type { GlobalAggregateOptions } from "..";
3+
import client from "./client";
4+
5+
async function getPageAggregateData(
6+
provider: PlausibleProvider,
7+
options?: GlobalAggregateOptions
8+
) {
9+
const plausibleClient = client(provider, {
10+
endpoint: "/stats/aggregate",
11+
timeframe: options?.timeframe,
12+
});
13+
14+
const data = await plausibleClient.fetch().then((response) => {
15+
return response.json();
16+
});
17+
18+
return data;
19+
}
20+
21+
export default getPageAggregateData;

0 commit comments

Comments
 (0)