PluginProbe ʕ •ᴥ•ʔ
Presto Player / 4.3.2
Presto Player v4.3.2
4.3.2 4.3.1 4.3.0 4.2.4 4.2.3 4.2.2 4.2.0 4.2.1 trunk 1.10.0 1.10.1 1.10.2 1.11.0 1.12.0 1.13.0 1.14.0 1.14.1 1.5.10 1.5.11 1.5.12 1.5.13 1.5.14 1.5.15 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.6.10 1.6.11 1.6.12 1.6.13 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.8.0 1.8.1 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.9.0 1.9.1 1.9.10 1.9.11 1.9.12 1.9.13 1.9.14 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.0.0 2.0.1 2.0.10 2.0.11 2.0.12 2.0.13 2.0.14 2.0.15 2.0.16 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1.0 2.2.0 2.2.1 2.2.2 2.2.3 2.2.3-beta1 2.3.0 2.3.1 2.3.2 2.3.3 3.0.0 3.0.0-beta1 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.8 3.1.0 3.1.1 3.1.2 3.1.3 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.0.5 4.0.6 4.0.7 4.0.8 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4
presto-player / src / admin / dashboard / components / TopPerformingMedia.js
presto-player / src / admin / dashboard / components Last commit date
Emails 1 month ago EngagementChart 2 months ago MediaHub 1 month ago Onboarding 2 months ago Popup 2 months ago Skeletons 1 month ago WhatsNew 2 months ago charts 2 months ago test 2 months ago AdminMenuSync.js 2 months ago ChartEmptyState.js 2 months ago ChooseDate.js 2 months ago ColorPicker.js 2 months ago ExtendPlugins.js 2 months ago Filters.js 2 months ago Link.js 2 months ago Navbar.js 1 month ago NoFound.js 2 months ago PageHeader.js 2 months ago PluginRecommendations.js 2 months ago PostScheduleField.js 2 months ago PrestoPlayerIcon.js 2 months ago ProGateOverlay.js 2 months ago QuickAccess.js 2 months ago RankedTable.js 2 months ago StatCard.js 2 months ago TopMedia.js 2 months ago TopPerformingMedia.js 2 months ago TopUsers.js 2 months ago TruncatedTitle.js 2 months ago UpgradeNotice.js 2 months ago UpgradeToPro.js 2 months ago VideoModal.js 2 months ago WelcomeBanner.js 2 months ago
TopPerformingMedia.js
136 lines
1 import React, { useState } from "react";
2 const { __ } = wp.i18n;
3 import { Label, Container, Select, Text } from "@bsf/force-ui";
4 import ChartEmptyState from "./ChartEmptyState";
5 import Link from "./Link";
6 import TruncatedTitle from "./TruncatedTitle";
7 import { useLocation } from "../router/router";
8
9 /**
10 * Top Performing widget showing top media or top users in a table.
11 * Pure presentational — data must be passed via the `data` prop.
12 */
13 const TopPerformingMedia = ({ data }) => {
14 const location = useLocation();
15 const fromTab = location.params?.tab || "Dashboard";
16
17 const topMedia = data?.topMedia || [];
18 const topUsers = data?.topUsers || [];
19
20 const [selectedId, setSelectedId] = useState("media");
21
22 const options = [
23 {
24 id: "media",
25 label: __("Most Watched", "presto-player"),
26 data: topMedia,
27 countLabel: __("Views", "presto-player"),
28 },
29 {
30 id: "users",
31 label: __("Most Engaged Viewers", "presto-player"),
32 data: topUsers,
33 countLabel: __("Views", "presto-player"),
34 },
35 ];
36
37 const selectedOption =
38 options.find((opt) => opt.id === selectedId) || options[0];
39
40 // Transformed data shape: media = { id, title, totalViews, avgViewTime }
41 // Transformed data shape: users = { id, name, totalViews, avgViewTime }
42 const getTitle = (item) => {
43 if (selectedId === "media") {
44 return item?.title || __("Untitled", "presto-player");
45 }
46 return item?.name || __("Unknown User", "presto-player");
47 };
48
49 return (
50 <div className="h-auto p-4 flex flex-col gap-3 border-[0.5px] border-border-subtle border-solid shadow-sm bg-background-primary rounded-xl">
51 {/* Header with inline Select */}
52 <div className="flex items-center justify-between gap-4">
53 <Label className="font-semibold">
54 {__("What's Performing", "presto-player")}
55 </Label>
56 <div className="min-w-[180px]">
57 <Select
58 size="sm"
59 value={selectedOption}
60 onChange={(option) => setSelectedId(option.id)}
61 by="id"
62 >
63 <Select.Button label="" render={(value) => value?.label || ""} />
64 <Select.Options>
65 {options.map((option) => (
66 <Select.Option key={option.id} value={option}>
67 {option.label}
68 </Select.Option>
69 ))}
70 </Select.Options>
71 </Select>
72 </div>
73 </div>
74
75 {/* Content */}
76 {selectedOption?.data?.length > 0 ? (
77 <div className="overflow-hidden rounded-lg border border-solid border-border-subtle">
78 {/* Header */}
79 <div className="flex items-center bg-background-secondary px-3 py-2">
80 <span className="flex-1 text-sm font-medium text-text-secondary">
81 {selectedId === "media"
82 ? __("Media", "presto-player")
83 : __("Viewer", "presto-player")}
84 </span>
85 <span className="w-10 text-right text-sm font-medium text-text-secondary">
86 {selectedOption.countLabel}
87 </span>
88 </div>
89
90 {/* Rows */}
91 {selectedOption.data.map((item, index) => {
92 const title = getTitle(item);
93 const hasLink = !!item?.id;
94
95 return (
96 <div
97 key={item?.id || index}
98 className="flex items-center px-3 py-3 border-t border-solid border-border-subtle border-b-0 border-x-0"
99 >
100 <div className="flex-1 min-w-0">
101 {hasLink ? (
102 <Link
103 params={{
104 page: "presto-dashboard",
105 tab: "Analytics",
106 detail: selectedId === "media" ? "media" : "user",
107 id: String(item.id),
108 from: fromTab,
109 }}
110 >
111 <a className="cursor-pointer no-underline hover:no-underline block min-w-0">
112 <TruncatedTitle title={title} />
113 </a>
114 </Link>
115 ) : (
116 <TruncatedTitle title={title} />
117 )}
118 </div>
119 <div className="w-10 text-right shrink-0 whitespace-nowrap">
120 <Text as="span" size="sm" className="text-text-secondary">
121 {item?.totalViews || "0"}
122 </Text>
123 </div>
124 </div>
125 );
126 })}
127 </div>
128 ) : (
129 <ChartEmptyState className="min-h-[112px] pt-6 pb-8" proGated />
130 )}
131 </div>
132 );
133 };
134
135 export default TopPerformingMedia;
136