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 / TopMedia.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
TopMedia.js
144 lines
1 import { useState } from "@wordpress/element";
2 const { __ } = wp.i18n;
3 import { Button, Badge, Text } from "@bsf/force-ui";
4 import { ArrowRight } from "lucide-react";
5 import { useHistory } from "../router/router";
6 import iconWhiteSvg from "../../../../img/icon-white.svg";
7 import ChartEmptyState from "./ChartEmptyState";
8 import TruncatedTitle from "./TruncatedTitle";
9 import RankedTable from "./RankedTable";
10
11 const MediaThumbnail = ({ item }) => {
12 const [imageError, setImageError] = useState(false);
13
14 return (
15 <div className="flex items-center gap-3 min-w-0">
16 {item?.poster_image && !imageError ? (
17 <img
18 src={item.poster_image}
19 alt={item?.title || ""}
20 className="flex-shrink-0 object-cover w-[75px] h-auto aspect-video rounded-[2px]"
21 loading="lazy"
22 onError={() => setImageError(true)}
23 />
24 ) : (
25 <div className="flex-shrink-0 flex items-center justify-center w-[75px] aspect-video box-border bg-[#d1d5db] rounded-[2px]">
26 <img
27 src={iconWhiteSvg}
28 alt=""
29 width="20"
30 height="auto"
31 className="h-auto"
32 />
33 </div>
34 )}
35 <TruncatedTitle title={item.title} className="flex-1" />
36 </div>
37 );
38 };
39
40 /**
41 * Top Media table with server-side pagination.
42 *
43 * @param {Object} props
44 * @param {Array} props.media Current page of media items.
45 * @param {number} props.currentPage 1-indexed page.
46 * @param {number} props.totalItems Total items across pages.
47 * @param {number} props.totalPages Total page count.
48 * @param {number} props.perPage Items per page.
49 * @param {Function} props.onPageChange (page) => void.
50 * @param {string} [props.fromUserId] Optional user id for back-navigation.
51 */
52 const TopMedia = ({
53 media = [],
54 currentPage = 1,
55 totalItems = 0,
56 totalPages = 0,
57 perPage = 10,
58 onPageChange,
59 fromUserId,
60 }) => {
61 const history = useHistory();
62
63 const goToDetail = (item) =>
64 history.push({
65 tab: "Analytics",
66 detail: "media",
67 id: String(item.id),
68 ...(fromUserId ? { from_user: String(fromUserId) } : {}),
69 });
70
71 const columns = [
72 {
73 key: "title",
74 header: __("Media", "presto-player"),
75 headerClassName: "text-text-secondary font-medium py-2",
76 cellClassName: "py-3",
77 render: (item) => <MediaThumbnail item={item} />,
78 },
79 {
80 key: "totalViews",
81 header: __("Views", "presto-player"),
82 headerClassName: "whitespace-nowrap text-text-secondary font-medium py-2",
83 cellClassName: "whitespace-nowrap py-2",
84 render: (item) => (
85 <Text as="span" size="sm" className="text-text-secondary">
86 {`${item.totalViews} ${__("views", "presto-player")}`}
87 </Text>
88 ),
89 },
90 {
91 key: "avgViewTime",
92 header: __("Avg View Time", "presto-player"),
93 headerClassName: "whitespace-nowrap text-text-secondary font-medium py-2",
94 cellClassName: "whitespace-nowrap py-2",
95 render: (item) => (
96 <Badge
97 label={item.avgViewTime}
98 type="rounded"
99 size="sm"
100 variant="neutral"
101 className="border-0 bg-gray-100 w-fit"
102 />
103 ),
104 },
105 ];
106
107 return (
108 <RankedTable
109 title={__("Most Watched", "presto-player")}
110 columns={columns}
111 rows={media}
112 rowKey={(item) => item.id}
113 onRowClick={goToDetail}
114 rowAction={(item) => (
115 <Button
116 variant="ghost"
117 size="xs"
118 className="text-brand-primary font-semibold"
119 iconPosition="right"
120 icon={<ArrowRight className="size-3" />}
121 onClick={(e) => {
122 e.stopPropagation();
123 goToDetail(item);
124 }}
125 >
126 {__("View Details", "presto-player")}
127 </Button>
128 )}
129 pagination={{
130 currentPage,
131 totalItems: totalItems || media.length,
132 totalPages: totalPages || Math.ceil((totalItems || media.length) / perPage),
133 perPage,
134 onPageChange,
135 }}
136 emptyState={
137 <ChartEmptyState className="min-h-[180px] pb-6 px-1" proGated />
138 }
139 />
140 );
141 };
142
143 export default TopMedia;
144