test
1 month ago
analyticsApi.js
1 month ago
classnames.js
1 month ago
dateUtils.js
1 month ago
formatters.js
1 month ago
helpers.js
1 month ago
icons.js
1 month ago
index.js
1 month ago
pluginUtils.js
1 month ago
transformers.js
1 month ago
viewportBoundary.js
1 month ago
formatters.js
151 lines
| 1 | /** |
| 2 | * Utility functions for formatting text, HTML, and time values |
| 3 | */ |
| 4 | |
| 5 | /** |
| 6 | * Decodes HTML entities in text |
| 7 | * Useful for rendering WordPress content that may contain encoded entities |
| 8 | * |
| 9 | * @param {string} text - The text containing HTML entities |
| 10 | * @returns {string} The decoded text |
| 11 | * |
| 12 | * @example |
| 13 | * decodeHTMLEntities("John's Video") // Returns: "John's Video" |
| 14 | * decodeHTMLEntities("Sales & Marketing") // Returns: "Sales & Marketing" |
| 15 | */ |
| 16 | export const decodeHTMLEntities = (text) => { |
| 17 | if (!text) return ""; |
| 18 | const textArea = document.createElement("textarea"); |
| 19 | textArea.innerHTML = text; |
| 20 | return textArea.value; |
| 21 | }; |
| 22 | |
| 23 | /** |
| 24 | * Converts seconds to a human-readable format |
| 25 | * |
| 26 | * @param {number} seconds - The number of seconds to format |
| 27 | * @returns {string} The formatted time string (e.g., "2h 30m 15s") |
| 28 | * |
| 29 | * @example |
| 30 | * humanSeconds(0) // Returns: "0s" |
| 31 | * humanSeconds(65) // Returns: "1m 5s" |
| 32 | * humanSeconds(3665) // Returns: "1h 1m 5s" |
| 33 | */ |
| 34 | /** |
| 35 | * Formats seconds as HH:MM:SS |
| 36 | * |
| 37 | * @param {number} seconds - The number of seconds to format |
| 38 | * @returns {string} Formatted time string (e.g., "01:30:05") |
| 39 | */ |
| 40 | export const formatHMS = (seconds) => { |
| 41 | const s = Math.round(seconds) || 0; |
| 42 | const h = Math.floor(s / 3600); |
| 43 | const m = Math.floor((s % 3600) / 60); |
| 44 | const sec = s % 60; |
| 45 | return [h, m, sec].map((v) => String(v).padStart(2, "0")).join(":"); |
| 46 | }; |
| 47 | |
| 48 | export const humanSeconds = (seconds) => { |
| 49 | if (!seconds || seconds === 0) { |
| 50 | return "0s"; |
| 51 | } |
| 52 | |
| 53 | const hours = Math.floor(seconds / 3600); |
| 54 | const minutes = Math.floor((seconds % 3600) / 60); |
| 55 | const secs = Math.floor(seconds % 60); |
| 56 | |
| 57 | const parts = []; |
| 58 | if (hours > 0) parts.push(`${hours}h`); |
| 59 | if (minutes > 0) parts.push(`${minutes}m`); |
| 60 | if (secs > 0 || parts.length === 0) parts.push(`${secs}s`); |
| 61 | |
| 62 | return parts.join(" "); |
| 63 | }; |
| 64 | |
| 65 | /** |
| 66 | * Compact single-unit version of humanSeconds for chart axes / tight UI. |
| 67 | * |
| 68 | * Stays in seconds up to 90s (so a sub-minute Y-axis reads "0s, 30s, 60s, 90s" |
| 69 | * rather than mixing "45s, 1m") and similarly defers to minutes up to 90m. |
| 70 | * |
| 71 | * @param {number} seconds |
| 72 | * @returns {string} e.g. "6s", "60s", "2m", "1h" |
| 73 | */ |
| 74 | export const humanSecondsCompact = (seconds) => { |
| 75 | const s = Math.round(Number(seconds) || 0); |
| 76 | if (s < 90) return `${s}s`; |
| 77 | if (s < 90 * 60) return `${Math.round(s / 60)}m`; |
| 78 | return `${Math.round(s / 3600)}h`; |
| 79 | }; |
| 80 | |
| 81 | /** |
| 82 | * Formats chart data for display in LineChart components |
| 83 | * Handles sorting, date formatting, and value transformations |
| 84 | * |
| 85 | * @param {Array} chartData - Array of chart data objects |
| 86 | * @param {string} dataKey - The key to extract the value from each data object |
| 87 | * @param {boolean} isWatchTime - Whether to convert seconds to minutes |
| 88 | * @param {Function} formatDate - Date formatting function |
| 89 | * @param {number} divisor - Number to divide values by (default: 60 for watch time) |
| 90 | * @param {number} decimalPlaces - Number of decimal places for watch time (default: 2) |
| 91 | * @returns {Array} Formatted chart data with 'month' and 'count' properties |
| 92 | * |
| 93 | * @example |
| 94 | * formatChartData(data, 'total', false, format) // For views |
| 95 | * formatChartData(data, 'total', true, format) // For watch time (converts seconds to minutes) |
| 96 | */ |
| 97 | export const formatChartData = ( |
| 98 | chartData, |
| 99 | dataKey, |
| 100 | isWatchTime = false, |
| 101 | formatDate, |
| 102 | divisor = 60, |
| 103 | decimalPlaces = 2 |
| 104 | ) => { |
| 105 | if (!chartData || chartData.length === 0) { |
| 106 | return []; |
| 107 | } |
| 108 | |
| 109 | const sortedChartData = [...chartData].sort( |
| 110 | (a, b) => new Date(a.date_time) - new Date(b.date_time) |
| 111 | ); |
| 112 | |
| 113 | return sortedChartData.map((data) => { |
| 114 | const value = parseInt(data[dataKey], 10) || 0; |
| 115 | return { |
| 116 | month: formatDate(new Date(data.date_time), "MMM dd, yyyy"), |
| 117 | count: isWatchTime |
| 118 | ? parseFloat((value / divisor).toFixed(decimalPlaces)) |
| 119 | : value, |
| 120 | }; |
| 121 | }); |
| 122 | }; |
| 123 | |
| 124 | /** |
| 125 | * Fill missing days in a formatted chart series with zero entries so the |
| 126 | * chart line touches y=0 across gaps instead of interpolating between the |
| 127 | * non-zero days surrounding them. |
| 128 | * |
| 129 | * @param {Array} points Output of formatChartData (each item has `{ month, count }`). |
| 130 | * @param {Object} range `{ from: Date, to: Date }` — inclusive day bounds. |
| 131 | * @param {Function} formatDate Same date formatter used by formatChartData. |
| 132 | * @returns {Array} Continuous daily series across the range. |
| 133 | */ |
| 134 | export const padDailyPoints = (points, range, formatDate) => { |
| 135 | if (!range?.from || !range?.to || !formatDate) return points; |
| 136 | |
| 137 | const byDay = new Map((points || []).map((p) => [p.month, p.count])); |
| 138 | const result = []; |
| 139 | const cursor = new Date(range.from); |
| 140 | cursor.setHours(0, 0, 0, 0); |
| 141 | const end = new Date(range.to); |
| 142 | end.setHours(0, 0, 0, 0); |
| 143 | |
| 144 | while (cursor <= end) { |
| 145 | const key = formatDate(cursor, "MMM dd, yyyy"); |
| 146 | result.push({ month: key, count: byDay.get(key) ?? 0 }); |
| 147 | cursor.setDate(cursor.getDate() + 1); |
| 148 | } |
| 149 | return result; |
| 150 | }; |
| 151 |