PluginProbe ʕ •ᴥ•ʔ
Presto Player / trunk
Presto Player vtrunk
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 / Emails / Utils.js
presto-player / src / admin / dashboard / components / Emails Last commit date
test 1 month ago Table.js 1 month ago Utils.js 1 month ago index.js 1 month ago
Utils.js
156 lines
1 /**
2 * Shared utils and constants for Emails page.
3 * Used by Table; matches MediaHub pattern (formatPublishDate, getBadge, truncation).
4 */
5 // eslint-disable-next-line import/no-extraneous-dependencies
6 import React from 'react';
7 const { __ } = wp.i18n;
8 import { Badge, Tooltip } from '@bsf/force-ui';
9
10 export const TRUNCATE_LENGTH = 40;
11
12 export const statusOptions = [
13 { label: __( 'All Status', 'presto-player' ), value: 'all' },
14 { label: __( 'Trashed', 'presto-player' ), value: 'trash' },
15 { label: __( 'Published', 'presto-player' ), value: 'publish' },
16 { label: __( 'Draft', 'presto-player' ), value: 'draft' },
17 { label: __( 'Pending Review', 'presto-player' ), value: 'pending' },
18 { label: __( 'Private', 'presto-player' ), value: 'private' },
19 { label: __( 'Scheduled', 'presto-player' ), value: 'future' },
20 ];
21
22 /**
23 * Truncate text with tooltip for full value (table cells). Returns "—" for empty.
24 *
25 * @param {string} text - Value to show.
26 * @param {number} maxLen - Max length before truncate (default TRUNCATE_LENGTH).
27 * @return {React.ReactNode} Truncated text with ellipsis tooltip, or full string, or em dash if empty.
28 */
29 export function renderTruncated( text, maxLen = TRUNCATE_LENGTH ) {
30 const str = text || '';
31 if ( ! str || str === '' ) {
32 return '';
33 }
34 if ( str.length <= maxLen ) {
35 return str;
36 }
37 const tooltipContent = (
38 <span
39 className="block max-w-[360px] break-all text-left"
40 style={ { whiteSpace: 'normal', wordBreak: 'break-all' } }
41 >
42 { str }
43 </span>
44 );
45 return (
46 <>
47 { str.slice( 0, maxLen - 1 ) }
48 <Tooltip content={ tooltipContent } arrow placement="top">
49 <span className="inline-block"></span>
50 </Tooltip>
51 </>
52 );
53 }
54
55 const statusBadgeConfig = {
56 publish: { label: __( 'Published', 'presto-player' ), variant: 'green' },
57 draft: { label: __( 'Draft', 'presto-player' ), variant: 'yellow' },
58 trash: { label: __( 'Trashed', 'presto-player' ), variant: 'red' },
59 pending: { label: __( 'Pending Review', 'presto-player' ), variant: 'blue' },
60 private: { label: __( 'Private', 'presto-player' ), variant: 'inverse' },
61 future: { label: __( 'Scheduled', 'presto-player' ), variant: 'blue' },
62 };
63
64 /**
65 * Status badge for table (publish, draft, pending, etc.). Matches MediaHub getBadge.
66 *
67 * @param {string} status - Post status.
68 * @return {React.ReactElement} Badge element for the status.
69 */
70 export function getBadge( status ) {
71 const { label, variant } = statusBadgeConfig[ status ] || {
72 label: __( 'Unknown', 'presto-player' ),
73 variant: 'gray',
74 };
75 return <Badge className="w-fit" variant={ variant } label={ label } />;
76 }
77
78 /**
79 * Format date string for table display (YYYY/MM/DD at h:mm am/pm). "Just Now" for invalid.
80 *
81 * @param {string} dateString - ISO or date string.
82 * @return {string} Formatted date (YYYY/MM/DD at h:mm am/pm) or "Just Now" if invalid.
83 */
84 export function formatPublishDate( dateString ) {
85 if ( ! dateString ) {
86 return __( 'Just Now', 'presto-player' );
87 }
88 const date = new Date( dateString );
89 if ( isNaN( date.getTime() ) ) {
90 return __( 'Just Now', 'presto-player' );
91 }
92 const yyyy = date.getFullYear();
93 const mm = String( date.getMonth() + 1 ).padStart( 2, '0' );
94 const dd = String( date.getDate() ).padStart( 2, '0' );
95 const hours = date.getHours();
96 const minutes = String( date.getMinutes() ).padStart( 2, '0' );
97 const ampm = hours >= 12 ? 'pm' : 'am';
98 const hour12 = hours % 12 || 12;
99 return `${ yyyy }/${ mm }/${ dd } at ${ hour12 }:${ minutes } ${ ampm }`;
100 }
101
102 /**
103 * Compute the next selection when the header bulk-select checkbox is
104 * toggled on a paginated table. Checking unions the current page's ids
105 * with the existing selection (so picks on other pages survive a page
106 * change); unchecking removes only the current page's ids.
107 *
108 * @param {Array<string|number>} prev - Currently selected ids.
109 * @param {Array<{ id: string|number }>} pageItems - Rows visible on the current page.
110 * @param {boolean} checked - true to add, false to remove.
111 * @return {Array<string|number>} The next selected-ids array.
112 */
113 export function togglePageSelection( prev, pageItems, checked ) {
114 const prevIds = prev || [];
115 const pageIds = ( pageItems || [] ).map( ( item ) => item.id );
116 if ( checked ) {
117 return Array.from( new Set( [ ...prevIds, ...pageIds ] ) );
118 }
119 return prevIds.filter( ( id ) => ! pageIds.includes( id ) );
120 }
121
122 /**
123 * Whether every row on the current page is in the selection. Empty page
124 * returns false so the header checkbox doesn't render checked when there
125 * is nothing on screen.
126 *
127 * @param {Array<{ id: string|number }>} pageItems
128 * @param {Array<string|number>} selected
129 * @return {boolean}
130 */
131 export function isPageFullySelected( pageItems, selected ) {
132 if ( ! pageItems?.length ) {
133 return false;
134 }
135 const selectedIds = selected || [];
136 return pageItems.every( ( item ) => selectedIds.includes( item.id ) );
137 }
138
139 /**
140 * Whether some — but not all — rows on the current page are in the
141 * selection. Drives the header checkbox's indeterminate state.
142 *
143 * @param {Array<{ id: string|number }>} pageItems
144 * @param {Array<string|number>} selected
145 * @return {boolean}
146 */
147 export function isPagePartiallySelected( pageItems, selected ) {
148 if ( ! pageItems?.length ) {
149 return false;
150 }
151 const selectedIds = selected || [];
152 const any = pageItems.some( ( item ) => selectedIds.includes( item.id ) );
153 const all = pageItems.every( ( item ) => selectedIds.includes( item.id ) );
154 return any && ! all;
155 }
156