@coursebuilder/analytics 1.1.1 → 1.1.2-canary.0.0c9beb2ae
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/index.d.ts +17 -1
- package/dist/components/index.js +110 -7
- package/dist/components/index.js.map +1 -1
- package/dist/engine.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js.map +1 -1
- package/dist/providers/database.d.ts +30 -0
- package/dist/providers/database.js +59 -0
- package/dist/providers/database.js.map +1 -1
- package/dist/providers/index.js +59 -0
- package/dist/providers/index.js.map +1 -1
- package/dist/types.d.ts +39 -1
- package/package.json +4 -4
- package/src/components/omnibus-dashboard.tsx +177 -6
- package/src/engine.ts +3 -2
- package/src/providers/database.ts +68 -0
- package/src/types.ts +49 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import { AnalyticsRange } from '../types.js';
|
|
2
|
+
import { TrafficBreakdownRow, AnalyticsRange } from '../types.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Data structure for the Mux video dashboard, inlined here so the shared
|
|
@@ -87,6 +87,15 @@ interface DashboardData {
|
|
|
87
87
|
totalUsers: number;
|
|
88
88
|
newUsers: number;
|
|
89
89
|
pageviews: number;
|
|
90
|
+
deviceCategories?: Array<TrafficBreakdownRow & {
|
|
91
|
+
deviceCategory: string;
|
|
92
|
+
}>;
|
|
93
|
+
operatingSystems?: Array<TrafficBreakdownRow & {
|
|
94
|
+
operatingSystem: string;
|
|
95
|
+
}>;
|
|
96
|
+
screenResolutions?: Array<TrafficBreakdownRow & {
|
|
97
|
+
screenResolution: string;
|
|
98
|
+
}>;
|
|
90
99
|
} | null;
|
|
91
100
|
attributionCoverage: {
|
|
92
101
|
totalRevenue: number;
|
|
@@ -94,6 +103,13 @@ interface DashboardData {
|
|
|
94
103
|
unattributedRevenue: number;
|
|
95
104
|
attributionRate: number;
|
|
96
105
|
totalPurchases: number;
|
|
106
|
+
quality?: Record<'strong' | 'medium' | 'weak' | 'unknown', {
|
|
107
|
+
lane: 'strong' | 'medium' | 'weak' | 'unknown';
|
|
108
|
+
purchases: number;
|
|
109
|
+
revenue: number;
|
|
110
|
+
revenuePercent: number;
|
|
111
|
+
notes: string[];
|
|
112
|
+
}>;
|
|
97
113
|
} | null;
|
|
98
114
|
mux: VideoDashboardData | null;
|
|
99
115
|
muxThumbnails: Record<string, string>;
|
package/dist/components/index.js
CHANGED
|
@@ -684,6 +684,88 @@ function ShortlinksCard({ shortlinks, totalClicks }) {
|
|
|
684
684
|
}), expanded ? "Show less" : `Show ${shortlinks.length - 10} more`)));
|
|
685
685
|
}
|
|
686
686
|
__name(ShortlinksCard, "ShortlinksCard");
|
|
687
|
+
function fmtPct(v) {
|
|
688
|
+
return `${v.toFixed(1)}%`;
|
|
689
|
+
}
|
|
690
|
+
__name(fmtPct, "fmtPct");
|
|
691
|
+
function TrafficBreakdownTable({ title, label, rows, getName }) {
|
|
692
|
+
if (!rows.length)
|
|
693
|
+
return null;
|
|
694
|
+
return /* @__PURE__ */ React3.createElement("section", {
|
|
695
|
+
className: "min-w-0"
|
|
696
|
+
}, /* @__PURE__ */ React3.createElement("div", {
|
|
697
|
+
className: "mb-2 flex items-baseline justify-between gap-2"
|
|
698
|
+
}, /* @__PURE__ */ React3.createElement("h3", {
|
|
699
|
+
className: "text-sm font-semibold"
|
|
700
|
+
}, title), /* @__PURE__ */ React3.createElement("span", {
|
|
701
|
+
className: "text-muted-foreground text-[11px]"
|
|
702
|
+
}, "Top ", rows.length)), /* @__PURE__ */ React3.createElement("div", {
|
|
703
|
+
className: "overflow-x-auto rounded-xl border border-border/50"
|
|
704
|
+
}, /* @__PURE__ */ React3.createElement("table", {
|
|
705
|
+
className: "w-full min-w-[440px] text-left text-sm"
|
|
706
|
+
}, /* @__PURE__ */ React3.createElement("thead", {
|
|
707
|
+
className: "bg-muted/30 text-muted-foreground text-[11px] uppercase tracking-wide"
|
|
708
|
+
}, /* @__PURE__ */ React3.createElement("tr", null, /* @__PURE__ */ React3.createElement("th", {
|
|
709
|
+
className: "px-3 py-2 font-medium"
|
|
710
|
+
}, label), /* @__PURE__ */ React3.createElement("th", {
|
|
711
|
+
className: "px-3 py-2 text-right font-medium"
|
|
712
|
+
}, "Sessions"), /* @__PURE__ */ React3.createElement("th", {
|
|
713
|
+
className: "px-3 py-2 text-right font-medium"
|
|
714
|
+
}, "Users"), /* @__PURE__ */ React3.createElement("th", {
|
|
715
|
+
className: "px-3 py-2 text-right font-medium"
|
|
716
|
+
}, "In rows"), /* @__PURE__ */ React3.createElement("th", {
|
|
717
|
+
className: "px-3 py-2 text-right font-medium"
|
|
718
|
+
}, "Of traffic"))), /* @__PURE__ */ React3.createElement("tbody", null, rows.map((row) => /* @__PURE__ */ React3.createElement("tr", {
|
|
719
|
+
key: getName(row),
|
|
720
|
+
className: "border-t border-border/40 tabular-nums"
|
|
721
|
+
}, /* @__PURE__ */ React3.createElement("td", {
|
|
722
|
+
className: "max-w-[220px] truncate px-3 py-2 font-medium"
|
|
723
|
+
}, getName(row)), /* @__PURE__ */ React3.createElement("td", {
|
|
724
|
+
className: "px-3 py-2 text-right"
|
|
725
|
+
}, row.sessions.toLocaleString()), /* @__PURE__ */ React3.createElement("td", {
|
|
726
|
+
className: "px-3 py-2 text-right"
|
|
727
|
+
}, row.users.toLocaleString()), /* @__PURE__ */ React3.createElement("td", {
|
|
728
|
+
className: "px-3 py-2 text-right"
|
|
729
|
+
}, fmtPct(row.sessionPercent)), /* @__PURE__ */ React3.createElement("td", {
|
|
730
|
+
className: "px-3 py-2 text-right"
|
|
731
|
+
}, fmtPct(row.trafficSessionPercent))))))));
|
|
732
|
+
}
|
|
733
|
+
__name(TrafficBreakdownTable, "TrafficBreakdownTable");
|
|
734
|
+
function TrafficBreakdownsCard({ traffic }) {
|
|
735
|
+
const deviceCategories = traffic.deviceCategories ?? [];
|
|
736
|
+
const operatingSystems = traffic.operatingSystems ?? [];
|
|
737
|
+
const screenResolutions = traffic.screenResolutions ?? [];
|
|
738
|
+
if (deviceCategories.length === 0 && operatingSystems.length === 0 && screenResolutions.length === 0) {
|
|
739
|
+
return null;
|
|
740
|
+
}
|
|
741
|
+
return /* @__PURE__ */ React3.createElement(Card, null, /* @__PURE__ */ React3.createElement(CardHeader, null, /* @__PURE__ */ React3.createElement("div", {
|
|
742
|
+
className: "flex items-center gap-2.5"
|
|
743
|
+
}, /* @__PURE__ */ React3.createElement(GlobeIcon, {
|
|
744
|
+
className: "text-muted-foreground h-4 w-4"
|
|
745
|
+
}), /* @__PURE__ */ React3.createElement("div", null, /* @__PURE__ */ React3.createElement(CardTitle, {
|
|
746
|
+
className: "text-sm font-semibold"
|
|
747
|
+
}, "Traffic Details"), /* @__PURE__ */ React3.createElement(CardDescription, {
|
|
748
|
+
className: "text-[11px]"
|
|
749
|
+
}, "Device, OS, and screen share. In rows is share inside the table. Of traffic is share of total sessions.")))), /* @__PURE__ */ React3.createElement(CardContent, null, /* @__PURE__ */ React3.createElement("div", {
|
|
750
|
+
className: "grid gap-4 xl:grid-cols-3"
|
|
751
|
+
}, /* @__PURE__ */ React3.createElement(TrafficBreakdownTable, {
|
|
752
|
+
title: "Device category",
|
|
753
|
+
label: "Device",
|
|
754
|
+
rows: deviceCategories,
|
|
755
|
+
getName: (row) => row.deviceCategory
|
|
756
|
+
}), /* @__PURE__ */ React3.createElement(TrafficBreakdownTable, {
|
|
757
|
+
title: "Operating systems",
|
|
758
|
+
label: "OS",
|
|
759
|
+
rows: operatingSystems.slice(0, 10),
|
|
760
|
+
getName: (row) => row.operatingSystem
|
|
761
|
+
}), /* @__PURE__ */ React3.createElement(TrafficBreakdownTable, {
|
|
762
|
+
title: "Screen resolutions",
|
|
763
|
+
label: "Resolution",
|
|
764
|
+
rows: screenResolutions.slice(0, 10),
|
|
765
|
+
getName: (row) => row.screenResolution
|
|
766
|
+
}))));
|
|
767
|
+
}
|
|
768
|
+
__name(TrafficBreakdownsCard, "TrafficBreakdownsCard");
|
|
687
769
|
var RANGES = [
|
|
688
770
|
{
|
|
689
771
|
value: "24h",
|
|
@@ -743,7 +825,7 @@ function OmnibusDashboard({ data, initialRange, appName, surveyDrilldownHref })
|
|
|
743
825
|
className: "text-pretty text-xl font-bold tracking-tight sm:text-2xl"
|
|
744
826
|
}, "Analytics"), /* @__PURE__ */ React3.createElement("p", {
|
|
745
827
|
className: "text-muted-foreground truncate text-[13px]"
|
|
746
|
-
}, "Revenue \xB7 Attribution \xB7 Video \xB7 Traffic \xB7 Surveys
|
|
828
|
+
}, "Revenue \xB7 Attribution \xB7 Video \xB7 Traffic \xB7 Surveys, ", rangeLabel)), /* @__PURE__ */ React3.createElement("div", {
|
|
747
829
|
className: "flex shrink-0 items-center gap-2"
|
|
748
830
|
}, isPending && /* @__PURE__ */ React3.createElement("span", {
|
|
749
831
|
className: "text-muted-foreground animate-pulse text-xs"
|
|
@@ -798,7 +880,9 @@ function OmnibusDashboard({ data, initialRange, appName, surveyDrilldownHref })
|
|
|
798
880
|
value: `${data.surveySegments.length}`,
|
|
799
881
|
sub: `${data.surveySegments.reduce((s, q) => s + q.responses, 0).toLocaleString()} responses`,
|
|
800
882
|
icon: ClipboardListIcon
|
|
801
|
-
})),
|
|
883
|
+
})), hasTraffic && /* @__PURE__ */ React3.createElement(TrafficBreakdownsCard, {
|
|
884
|
+
traffic: data.traffic
|
|
885
|
+
}), hasMux && data.mux.topVideos.length > 0 && /* @__PURE__ */ React3.createElement(TopVideosCard, {
|
|
802
886
|
label: "Site Videos",
|
|
803
887
|
subtitle: `${fmtWatchMs(data.mux.overview.totalPlayingTimeMs)} watch time \xB7 ${fmtK(data.mux.overview.uniqueViewers)} viewers`,
|
|
804
888
|
icon: FilmIcon,
|
|
@@ -840,7 +924,7 @@ function OmnibusDashboard({ data, initialRange, appName, surveyDrilldownHref })
|
|
|
840
924
|
className: "text-muted-foreground block text-[11px] font-medium uppercase tracking-wider"
|
|
841
925
|
}, "Conversion Rate"), /* @__PURE__ */ React3.createElement("span", {
|
|
842
926
|
className: "text-foreground mt-1 block text-xl font-bold tabular-nums"
|
|
843
|
-
}, signupCount > 0 ? `${(purchaseAttrCount / signupCount * 100).toFixed(1)}%` : "
|
|
927
|
+
}, signupCount > 0 ? `${(purchaseAttrCount / signupCount * 100).toFixed(1)}%` : "-"))))), hasAttributionCoverage && data.attributionCoverage.totalPurchases > 0 && /* @__PURE__ */ React3.createElement(Card, null, /* @__PURE__ */ React3.createElement(CardHeader, {
|
|
844
928
|
className: "pb-3"
|
|
845
929
|
}, /* @__PURE__ */ React3.createElement("div", {
|
|
846
930
|
className: "flex items-center gap-2.5"
|
|
@@ -872,7 +956,26 @@ function OmnibusDashboard({ data, initialRange, appName, surveyDrilldownHref })
|
|
|
872
956
|
className: "text-muted-foreground block text-[11px] font-medium uppercase tracking-wider"
|
|
873
957
|
}, "Total Purchases"), /* @__PURE__ */ React3.createElement("span", {
|
|
874
958
|
className: "text-foreground mt-1 block text-xl font-bold tabular-nums"
|
|
875
|
-
}, data.attributionCoverage.totalPurchases.toLocaleString())))
|
|
959
|
+
}, data.attributionCoverage.totalPurchases.toLocaleString()))), data.attributionCoverage.quality && /* @__PURE__ */ React3.createElement("div", {
|
|
960
|
+
className: "mt-4 grid gap-2 sm:grid-cols-4"
|
|
961
|
+
}, [
|
|
962
|
+
"strong",
|
|
963
|
+
"medium",
|
|
964
|
+
"weak",
|
|
965
|
+
"unknown"
|
|
966
|
+
].map((lane) => {
|
|
967
|
+
const item = data.attributionCoverage.quality[lane];
|
|
968
|
+
return /* @__PURE__ */ React3.createElement("div", {
|
|
969
|
+
key: lane,
|
|
970
|
+
className: "rounded-lg border border-border/40 p-3"
|
|
971
|
+
}, /* @__PURE__ */ React3.createElement("span", {
|
|
972
|
+
className: "text-muted-foreground block text-[11px] font-medium uppercase tracking-wider"
|
|
973
|
+
}, lane), /* @__PURE__ */ React3.createElement("span", {
|
|
974
|
+
className: "text-foreground mt-1 block text-lg font-bold tabular-nums"
|
|
975
|
+
}, fmt$(item.revenue)), /* @__PURE__ */ React3.createElement("span", {
|
|
976
|
+
className: "text-muted-foreground text-[11px]"
|
|
977
|
+
}, item.purchases.toLocaleString(), " purchases \xB7", " ", (item.revenuePercent * 100).toFixed(1), "%"));
|
|
978
|
+
})))), hasValuePaths && /* @__PURE__ */ React3.createElement(Card, null, /* @__PURE__ */ React3.createElement(CardHeader, {
|
|
876
979
|
className: "pb-3"
|
|
877
980
|
}, /* @__PURE__ */ React3.createElement("div", {
|
|
878
981
|
className: "flex items-center gap-2.5"
|
|
@@ -1327,13 +1430,13 @@ function OmnibusDashboard({ data, initialRange, appName, surveyDrilldownHref })
|
|
|
1327
1430
|
className: `py-2.5 pr-4 text-right text-sm font-semibold tabular-nums ${p.totalAmount > 0 ? "text-emerald-600 dark:text-emerald-400" : "text-muted-foreground"}`
|
|
1328
1431
|
}, p.totalAmount > 0 ? fmt$(p.totalAmount) : "Free"), /* @__PURE__ */ React3.createElement("td", {
|
|
1329
1432
|
className: "py-2.5 pr-4 text-right text-sm tabular-nums"
|
|
1330
|
-
}, p.seats ?? "
|
|
1433
|
+
}, p.seats ?? "-"), /* @__PURE__ */ React3.createElement("td", {
|
|
1331
1434
|
className: "max-w-[200px] truncate py-2.5 pr-4 text-sm font-medium"
|
|
1332
1435
|
}, p.productName), /* @__PURE__ */ React3.createElement("td", {
|
|
1333
1436
|
className: "text-muted-foreground hidden max-w-[150px] truncate py-2.5 pr-4 text-sm sm:table-cell"
|
|
1334
|
-
}, p.userName ?? p.userEmail ?? "
|
|
1437
|
+
}, p.userName ?? p.userEmail ?? "-"), /* @__PURE__ */ React3.createElement("td", {
|
|
1335
1438
|
className: "text-muted-foreground hidden py-2.5 pr-4 text-sm sm:table-cell"
|
|
1336
|
-
}, p.country ? `${countryFlag2(p.country)} ${p.country}` : "
|
|
1439
|
+
}, p.country ? `${countryFlag2(p.country)} ${p.country}` : "-"), /* @__PURE__ */ React3.createElement("td", {
|
|
1337
1440
|
className: "text-muted-foreground whitespace-nowrap py-2.5 pr-4 text-sm"
|
|
1338
1441
|
}, fmtAgo(p.createdAt)))))))))));
|
|
1339
1442
|
}
|