@levi-gemcommerce/analytics 0.0.1-dev.7 → 0.0.1-dev.8
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/esm/components/GSelectableMetricChartCard/GSelectableMetricChartCard.d.ts +5 -3
- package/dist/esm/components/GSelectableMetricChartCard/MetricChartTab.d.ts +2 -2
- package/dist/esm/components/MetricDonutChartCard/MetricDonutChartCard.d.ts +16 -0
- package/dist/esm/components/MetricDonutChartCard/index.d.ts +2 -0
- package/dist/esm/components/MetricDonutChartCard/utils/donut-chart.d.ts +20 -0
- package/dist/esm/components/MetricDonutChartCard/utils/index.d.ts +1 -0
- package/dist/esm/components/SingleMetricChartCard/SingleMetricChartCard.d.ts +3 -1
- package/dist/esm/components/common/chart/MetricChart.d.ts +5 -1
- package/dist/esm/components/common/chart/MetricDonutChartSkeleton.d.ts +1 -0
- package/dist/esm/components/common/chart/index.d.ts +1 -0
- package/dist/esm/components/index.d.ts +1 -0
- package/dist/esm/constants/breakdown-targets.d.ts +5 -0
- package/dist/esm/constants/index.d.ts +1 -0
- package/dist/esm/core/gemxql/helpers/extractQueryData.d.ts +1 -1
- package/dist/esm/hooks/index.d.ts +2 -0
- package/dist/esm/hooks/useFormatLineChartData.d.ts +11 -0
- package/dist/esm/hooks/useWindowSize.d.ts +18 -0
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +668 -52
- package/dist/esm/index.mjs +668 -52
- package/dist/esm/providers/MetricChartProvider.d.ts +5 -1
- package/dist/esm/types/chart.d.ts +15 -1
- package/dist/esm/types/metric.d.ts +4 -1
- package/dist/esm/types.js +3 -0
- package/dist/esm/types.mjs +3 -0
- package/dist/style.css +1 -1
- package/dist/umd/esm/components/GSelectableMetricChartCard/GSelectableMetricChartCard.d.ts +5 -3
- package/dist/umd/esm/components/GSelectableMetricChartCard/MetricChartTab.d.ts +2 -2
- package/dist/umd/esm/components/MetricDonutChartCard/MetricDonutChartCard.d.ts +16 -0
- package/dist/umd/esm/components/MetricDonutChartCard/index.d.ts +2 -0
- package/dist/umd/esm/components/MetricDonutChartCard/utils/donut-chart.d.ts +20 -0
- package/dist/umd/esm/components/MetricDonutChartCard/utils/index.d.ts +1 -0
- package/dist/umd/esm/components/SingleMetricChartCard/SingleMetricChartCard.d.ts +3 -1
- package/dist/umd/esm/components/common/chart/MetricChart.d.ts +5 -1
- package/dist/umd/esm/components/common/chart/MetricDonutChartSkeleton.d.ts +1 -0
- package/dist/umd/esm/components/common/chart/index.d.ts +1 -0
- package/dist/umd/esm/components/index.d.ts +1 -0
- package/dist/umd/esm/constants/breakdown-targets.d.ts +5 -0
- package/dist/umd/esm/constants/index.d.ts +1 -0
- package/dist/umd/esm/core/gemxql/helpers/extractQueryData.d.ts +1 -1
- package/dist/umd/esm/hooks/index.d.ts +2 -0
- package/dist/umd/esm/hooks/useFormatLineChartData.d.ts +11 -0
- package/dist/umd/esm/hooks/useWindowSize.d.ts +18 -0
- package/dist/umd/esm/index.d.ts +2 -0
- package/dist/umd/esm/providers/MetricChartProvider.d.ts +5 -1
- package/dist/umd/esm/types/chart.d.ts +15 -1
- package/dist/umd/esm/types/metric.d.ts +4 -1
- package/dist/umd/index.js +1 -1
- package/dist/umd/types.js +1 -1
- package/package.json +1 -1
package/dist/esm/index.mjs
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
3
|
+
import '@tanstack/react-query';
|
|
4
|
+
import dayjs from 'dayjs';
|
|
5
|
+
import quarterOfYear from 'dayjs/plugin/quarterOfYear';
|
|
6
|
+
import timezone from 'dayjs/plugin/timezone';
|
|
7
|
+
import utc from 'dayjs/plugin/utc';
|
|
8
|
+
import React, { useMemo, forwardRef, useState, useRef, useImperativeHandle, useEffect } from 'react';
|
|
3
9
|
import { SkeletonDisplayText, Box, Popover, InlineStack, BlockStack, Text, List, Card, SkeletonBodyText, Icon } from '@shopify/polaris';
|
|
4
|
-
import
|
|
5
|
-
import { PolarisVizProvider, LineChart } from '@shopify/polaris-viz';
|
|
10
|
+
import { PolarisVizProvider, LineChart, DonutChart } from '@shopify/polaris-viz';
|
|
6
11
|
|
|
7
12
|
var EMetricKey;
|
|
8
13
|
(function (EMetricKey) {
|
|
@@ -22,6 +27,9 @@ var EMetricKey;
|
|
|
22
27
|
EMetricKey["AOV"] = "aov";
|
|
23
28
|
EMetricKey["REVENUE"] = "revenue";
|
|
24
29
|
EMetricKey["RPV"] = "revenue_per_visitor";
|
|
30
|
+
EMetricKey["VISITOR_ITEMS"] = "visitor_items";
|
|
31
|
+
EMetricKey["DEVICE_ITEMS"] = "device_items";
|
|
32
|
+
EMetricKey["TRAFFIC_SOURCE_ITEMS"] = "traffic_source_items";
|
|
25
33
|
})(EMetricKey || (EMetricKey = {}));
|
|
26
34
|
|
|
27
35
|
const ANALYTICS_METRIC_TOOLTIP = {
|
|
@@ -97,8 +105,524 @@ const ANALYTICS_METRIC_TOOLTIP = {
|
|
|
97
105
|
title: 'Sessions with cart additions',
|
|
98
106
|
content: 'Sessions in your online store in which a visitor added item to the cart',
|
|
99
107
|
},
|
|
108
|
+
[EMetricKey.VISITOR_ITEMS]: {
|
|
109
|
+
title: 'Sessions by visitor type',
|
|
110
|
+
content: '<p>Numbers of new and returning visitors</p>',
|
|
111
|
+
contentList: [
|
|
112
|
+
'New visitor: who accesses your store for the first time after GemX installation',
|
|
113
|
+
'Returning visitor: who comes back to your store after GemX installation',
|
|
114
|
+
],
|
|
115
|
+
},
|
|
116
|
+
[EMetricKey.DEVICE_ITEMS]: {
|
|
117
|
+
title: 'Sessions by device type',
|
|
118
|
+
content: `Sessions in your online store by the visitor's device type`,
|
|
119
|
+
},
|
|
120
|
+
[EMetricKey.TRAFFIC_SOURCE_ITEMS]: {
|
|
121
|
+
title: 'Sessions by traffic source',
|
|
122
|
+
content: 'Sessions on your page by where visitors come from',
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const NONE_VALUE = 'None';
|
|
127
|
+
const PLACEHOLDER_VALUE$1 = '-';
|
|
128
|
+
|
|
129
|
+
const TOTALS_SUFFIX = '___totals';
|
|
130
|
+
const COMPARE_PREFIX = 'comparison___';
|
|
131
|
+
const COMPARE_SUFFIX = '___previous_period';
|
|
132
|
+
const COMPARE_TOTALS_SUFFIX = `${COMPARE_SUFFIX}${TOTALS_SUFFIX}`;
|
|
133
|
+
|
|
134
|
+
const OPERATOR_IS = 'is';
|
|
135
|
+
|
|
136
|
+
var EAnalyticDataType;
|
|
137
|
+
(function (EAnalyticDataType) {
|
|
138
|
+
EAnalyticDataType["DATE"] = "DATE";
|
|
139
|
+
EAnalyticDataType["ARRAY"] = "ARRAY";
|
|
140
|
+
EAnalyticDataType["OBJECT"] = "OBJECT";
|
|
141
|
+
EAnalyticDataType["STRING"] = "STRING";
|
|
142
|
+
EAnalyticDataType["INTEGER"] = "INTEGER";
|
|
143
|
+
EAnalyticDataType["CURRENCY"] = "CURRENCY";
|
|
144
|
+
EAnalyticDataType["PERCENT"] = "PERCENT";
|
|
145
|
+
EAnalyticDataType["DURATION"] = "DURATION";
|
|
146
|
+
EAnalyticDataType["MONTH"] = "MONTH_TIMESTAMP";
|
|
147
|
+
EAnalyticDataType["QUARTER"] = "QUARTER_TIMESTAMP";
|
|
148
|
+
EAnalyticDataType["DAY"] = "DAY_TIMESTAMP";
|
|
149
|
+
EAnalyticDataType["WEEK"] = "WEEK_TIMESTAMP";
|
|
150
|
+
EAnalyticDataType["YEAR"] = "YEAR_TIMESTAMP";
|
|
151
|
+
EAnalyticDataType["HOUR"] = "HOUR_TIMESTAMP";
|
|
152
|
+
})(EAnalyticDataType || (EAnalyticDataType = {}));
|
|
153
|
+
var EAnalyticColumnKey;
|
|
154
|
+
(function (EAnalyticColumnKey) {
|
|
155
|
+
EAnalyticColumnKey["CAMPAIGNS"] = "experiments";
|
|
156
|
+
EAnalyticColumnKey["VISITOR_ITEMS"] = "visitor_items";
|
|
157
|
+
EAnalyticColumnKey["DEVICE_ITEMS"] = "device_items";
|
|
158
|
+
EAnalyticColumnKey["TRAFFIC_SOURCE_ITEMS"] = "traffic_source_items";
|
|
159
|
+
})(EAnalyticColumnKey || (EAnalyticColumnKey = {}));
|
|
160
|
+
|
|
161
|
+
var EAnalyticMode;
|
|
162
|
+
(function (EAnalyticMode) {
|
|
163
|
+
EAnalyticMode["ALL_SESSION"] = "ALL_SESSION";
|
|
164
|
+
EAnalyticMode["FIRST_SESSION"] = "FIRST_SESSION";
|
|
165
|
+
EAnalyticMode["PAGE_ONLY"] = "PAGE_ONLY";
|
|
166
|
+
})(EAnalyticMode || (EAnalyticMode = {}));
|
|
167
|
+
|
|
168
|
+
var EAnalyticSource;
|
|
169
|
+
(function (EAnalyticSource) {
|
|
170
|
+
EAnalyticSource["SESSIONS"] = "sessions";
|
|
171
|
+
EAnalyticSource["SALES"] = "sales";
|
|
172
|
+
})(EAnalyticSource || (EAnalyticSource = {}));
|
|
173
|
+
|
|
174
|
+
var EVisitorType;
|
|
175
|
+
(function (EVisitorType) {
|
|
176
|
+
EVisitorType["NEW"] = "new";
|
|
177
|
+
EVisitorType["RETURNING"] = "returning";
|
|
178
|
+
})(EVisitorType || (EVisitorType = {}));
|
|
179
|
+
var EDeviceType;
|
|
180
|
+
(function (EDeviceType) {
|
|
181
|
+
EDeviceType["DESKTOP"] = "desktop";
|
|
182
|
+
EDeviceType["MOBILE"] = "mobile";
|
|
183
|
+
EDeviceType["TABLET"] = "tablet";
|
|
184
|
+
})(EDeviceType || (EDeviceType = {}));
|
|
185
|
+
var ETrafficSourceType;
|
|
186
|
+
(function (ETrafficSourceType) {
|
|
187
|
+
ETrafficSourceType["DIRECT"] = "direct";
|
|
188
|
+
ETrafficSourceType["EMAIL"] = "email";
|
|
189
|
+
ETrafficSourceType["REFERRAL"] = "referral";
|
|
190
|
+
ETrafficSourceType["ORGANIC_SOCIAL"] = "organic-social";
|
|
191
|
+
ETrafficSourceType["ORGANIC_SEARCH"] = "organic-search";
|
|
192
|
+
ETrafficSourceType["PAID_SOCIAL"] = "paid-social";
|
|
193
|
+
ETrafficSourceType["PAID_SEARCH"] = "paid-search";
|
|
194
|
+
ETrafficSourceType["SMS"] = "sms";
|
|
195
|
+
})(ETrafficSourceType || (ETrafficSourceType = {}));
|
|
196
|
+
|
|
197
|
+
var EComparisonOperator;
|
|
198
|
+
(function (EComparisonOperator) {
|
|
199
|
+
EComparisonOperator["EQ"] = "=";
|
|
200
|
+
EComparisonOperator["IN"] = "IN";
|
|
201
|
+
EComparisonOperator["LIKE"] = "LIKE";
|
|
202
|
+
})(EComparisonOperator || (EComparisonOperator = {}));
|
|
203
|
+
var EGroupOperator;
|
|
204
|
+
(function (EGroupOperator) {
|
|
205
|
+
EGroupOperator["OR"] = "OR";
|
|
206
|
+
EGroupOperator["AND"] = "AND";
|
|
207
|
+
})(EGroupOperator || (EGroupOperator = {}));
|
|
208
|
+
|
|
209
|
+
var EFilterField;
|
|
210
|
+
(function (EFilterField) {
|
|
211
|
+
EFilterField["DEVICE"] = "device";
|
|
212
|
+
EFilterField["DEVICES"] = "devices";
|
|
213
|
+
EFilterField["VISITOR"] = "visitor_type";
|
|
214
|
+
EFilterField["VISITORS"] = "visitor_types";
|
|
215
|
+
EFilterField["TRAFFIC_SOURCE"] = "traffic_source";
|
|
216
|
+
EFilterField["TRAFFIC_SOURCES"] = "traffic_sources";
|
|
217
|
+
EFilterField["VERSION"] = "version";
|
|
218
|
+
EFilterField["VERSIONS"] = "versions";
|
|
219
|
+
EFilterField["SINGLE_PAGE"] = "page_path";
|
|
220
|
+
EFilterField["LIST_PAGE"] = "page_paths";
|
|
221
|
+
EFilterField["GROUP_CAMPAIGN_ITEM"] = "group_campaign_item";
|
|
222
|
+
EFilterField["GROUP_CAMPAIGN_ITEMS"] = "group_campaign_items";
|
|
223
|
+
EFilterField["SINGLE_CAMPAIGN"] = "experiment_id";
|
|
224
|
+
EFilterField["GROUP_CAMPAIGN"] = "experiment_group_id";
|
|
225
|
+
EFilterField["CAMPAIGN_VERSION_ID"] = "version_id";
|
|
226
|
+
EFilterField["GROUP_CAMPAIGN_VERSION_ID"] = "group_version_id";
|
|
227
|
+
})(EFilterField || (EFilterField = {}));
|
|
228
|
+
|
|
229
|
+
var EOperatorField;
|
|
230
|
+
(function (EOperatorField) {
|
|
231
|
+
EOperatorField["DEVICE_OPERATOR"] = "deviceOperator";
|
|
232
|
+
EOperatorField["VISITOR_OPERATOR"] = "visitorOperator";
|
|
233
|
+
EOperatorField["TRAFFIC_SOURCE_OPERATOR"] = "trafficSourceOperator";
|
|
234
|
+
EOperatorField["VERSION_OPERATOR"] = "versionOperator";
|
|
235
|
+
EOperatorField["PAGE_OPERATOR"] = "pageOperator";
|
|
236
|
+
EOperatorField["CAMPAIGN_ITEM_OPERATOR"] = "campaignItemOperator";
|
|
237
|
+
})(EOperatorField || (EOperatorField = {}));
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Controls which totals columns are appended to the query result.
|
|
241
|
+
*
|
|
242
|
+
* - NONE → no totals, GROUP BY only.
|
|
243
|
+
* - TOTALS → adds `<metric>___totals` (grand total only). Use with single-dimension GROUP BY.
|
|
244
|
+
* - ALL → adds subtotals per dimension group + grand total. Use with multi-dimension GROUP BY.
|
|
245
|
+
*/
|
|
246
|
+
var EGroupWithClause;
|
|
247
|
+
(function (EGroupWithClause) {
|
|
248
|
+
EGroupWithClause["NONE"] = "";
|
|
249
|
+
EGroupWithClause["TOTALS"] = "TOTALS";
|
|
250
|
+
EGroupWithClause["ALL"] = "WITH GROUP_TOTALS, TOTALS";
|
|
251
|
+
})(EGroupWithClause || (EGroupWithClause = {}));
|
|
252
|
+
|
|
253
|
+
var EOrderDirectionType;
|
|
254
|
+
(function (EOrderDirectionType) {
|
|
255
|
+
EOrderDirectionType["ASC"] = "ASC";
|
|
256
|
+
EOrderDirectionType["DESC"] = "DESC";
|
|
257
|
+
})(EOrderDirectionType || (EOrderDirectionType = {}));
|
|
258
|
+
|
|
259
|
+
var EPageMetric;
|
|
260
|
+
(function (EPageMetric) {
|
|
261
|
+
EPageMetric["PAGE_ITEMS"] = "page_items";
|
|
262
|
+
EPageMetric["PAGE_PATHS"] = "page_paths";
|
|
263
|
+
})(EPageMetric || (EPageMetric = {}));
|
|
264
|
+
var EPageDimension;
|
|
265
|
+
(function (EPageDimension) {
|
|
266
|
+
EPageDimension["PAGE_PATH"] = "page_path";
|
|
267
|
+
})(EPageDimension || (EPageDimension = {}));
|
|
268
|
+
var EPageField;
|
|
269
|
+
(function (EPageField) {
|
|
270
|
+
EPageField["SHOPIFY_PAGE_ID"] = "shopify_page_id";
|
|
271
|
+
EPageField["LOCATION_PATH"] = "location_path";
|
|
272
|
+
EPageField["PAGE_TYPE"] = "page_type";
|
|
273
|
+
EPageField["PAGE_TITLE"] = "page_title";
|
|
274
|
+
EPageField["PAGE_PATH"] = "page_path";
|
|
275
|
+
EPageField["TEMPLATE_SUFFIX"] = "template_suffix";
|
|
276
|
+
})(EPageField || (EPageField = {}));
|
|
277
|
+
|
|
278
|
+
var ERowReaderMode;
|
|
279
|
+
(function (ERowReaderMode) {
|
|
280
|
+
ERowReaderMode["DEFAULT"] = "DEFAULT";
|
|
281
|
+
ERowReaderMode["COMPARISON"] = "COMPARISON";
|
|
282
|
+
ERowReaderMode["TOTALS"] = "TOTALS";
|
|
283
|
+
ERowReaderMode["COMPARISON_TOTALS"] = "COMPARISON_TOTALS";
|
|
284
|
+
})(ERowReaderMode || (ERowReaderMode = {}));
|
|
285
|
+
|
|
286
|
+
var ETimeDimension;
|
|
287
|
+
(function (ETimeDimension) {
|
|
288
|
+
ETimeDimension["HOUR"] = "hour";
|
|
289
|
+
ETimeDimension["DAY"] = "day";
|
|
290
|
+
ETimeDimension["WEEK"] = "week";
|
|
291
|
+
ETimeDimension["MONTH"] = "month";
|
|
292
|
+
ETimeDimension["QUARTER"] = "quarter";
|
|
293
|
+
ETimeDimension["YEAR"] = "year";
|
|
294
|
+
})(ETimeDimension || (ETimeDimension = {}));
|
|
295
|
+
|
|
296
|
+
({
|
|
297
|
+
[ERowReaderMode.DEFAULT]: { prefix: '', suffix: '' },
|
|
298
|
+
[ERowReaderMode.COMPARISON]: { prefix: COMPARE_PREFIX, suffix: COMPARE_SUFFIX },
|
|
299
|
+
[ERowReaderMode.TOTALS]: { prefix: '', suffix: TOTALS_SUFFIX },
|
|
300
|
+
[ERowReaderMode.COMPARISON_TOTALS]: { prefix: COMPARE_PREFIX, suffix: COMPARE_TOTALS_SUFFIX },
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
EGroupOperator.OR;
|
|
304
|
+
|
|
305
|
+
[
|
|
306
|
+
{
|
|
307
|
+
operator: EOperatorField.DEVICE_OPERATOR,
|
|
308
|
+
singleField: EFilterField.DEVICE,
|
|
309
|
+
multiField: EFilterField.DEVICES,
|
|
310
|
+
fieldName: EFilterField.DEVICE,
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
operator: EOperatorField.VISITOR_OPERATOR,
|
|
314
|
+
singleField: EFilterField.VISITOR,
|
|
315
|
+
multiField: EFilterField.VISITORS,
|
|
316
|
+
fieldName: EFilterField.VISITOR,
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
operator: EOperatorField.TRAFFIC_SOURCE_OPERATOR,
|
|
320
|
+
singleField: EFilterField.TRAFFIC_SOURCE,
|
|
321
|
+
multiField: EFilterField.TRAFFIC_SOURCES,
|
|
322
|
+
fieldName: EFilterField.TRAFFIC_SOURCE,
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
operator: EOperatorField.VERSION_OPERATOR,
|
|
326
|
+
singleField: EFilterField.VERSION,
|
|
327
|
+
multiField: EFilterField.VERSIONS,
|
|
328
|
+
fieldName: EFilterField.VERSION,
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
operator: EOperatorField.PAGE_OPERATOR,
|
|
332
|
+
singleField: EFilterField.SINGLE_PAGE,
|
|
333
|
+
multiField: EFilterField.LIST_PAGE,
|
|
334
|
+
fieldName: EFilterField.SINGLE_PAGE,
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
operator: EOperatorField.CAMPAIGN_ITEM_OPERATOR,
|
|
338
|
+
singleField: EFilterField.GROUP_CAMPAIGN_ITEM,
|
|
339
|
+
multiField: EFilterField.GROUP_CAMPAIGN_ITEMS,
|
|
340
|
+
condition: EFilterField.GROUP_CAMPAIGN,
|
|
341
|
+
fieldName: EFilterField.SINGLE_CAMPAIGN,
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
operatorVal: OPERATOR_IS,
|
|
345
|
+
singleField: EFilterField.SINGLE_CAMPAIGN,
|
|
346
|
+
multiField: EFilterField.GROUP_CAMPAIGN,
|
|
347
|
+
},
|
|
348
|
+
];
|
|
349
|
+
|
|
350
|
+
dayjs.extend(utc);
|
|
351
|
+
dayjs.extend(timezone);
|
|
352
|
+
dayjs.extend(quarterOfYear);
|
|
353
|
+
let tz = 'UTC';
|
|
354
|
+
const dayjsTz = (date) => {
|
|
355
|
+
if (!date)
|
|
356
|
+
return dayjs().tz(tz);
|
|
357
|
+
return dayjs(date).tz(tz);
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
const TRIM_DECIMAL_ZEROS_REGEX = /\.0+$/;
|
|
361
|
+
const DEFAULT_DECIMALS = 2;
|
|
362
|
+
const trimDecimalZeros = (number) => {
|
|
363
|
+
return `${number}`.replace(TRIM_DECIMAL_ZEROS_REGEX, '');
|
|
364
|
+
};
|
|
365
|
+
const cleanDecimal = (number, decimals = DEFAULT_DECIMALS) => {
|
|
366
|
+
return trimDecimalZeros(number.toFixed(decimals));
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Utility function to calculate percentage and format it.
|
|
371
|
+
* @param part - The part value.
|
|
372
|
+
* @param total - The total value.
|
|
373
|
+
* @param decimals - The number of decimal places to format the percentage to.
|
|
374
|
+
* @returns The formatted percentage as a string.
|
|
375
|
+
*/
|
|
376
|
+
const PERCENTAGE_THRESHOLD = 0.005;
|
|
377
|
+
const PERCENTAGE_THRESHOLD_STRING = '~0%';
|
|
378
|
+
const calcPercentage = (part, total, decimals = DEFAULT_DECIMALS) => {
|
|
379
|
+
if (typeof part !== 'number' || !total)
|
|
380
|
+
return undefined;
|
|
381
|
+
if (part === 0)
|
|
382
|
+
return 0;
|
|
383
|
+
const percentage = (part / total) * 100;
|
|
384
|
+
return parseFloat(cleanDecimal(percentage, decimals));
|
|
385
|
+
};
|
|
386
|
+
const isLessThanThreshold = (percentage) => percentage > 0 && percentage < PERCENTAGE_THRESHOLD;
|
|
387
|
+
const calcPercentageString = (part, total, decimals = 2) => {
|
|
388
|
+
const percentage = calcPercentage(part, total, decimals);
|
|
389
|
+
if (typeof percentage !== 'number')
|
|
390
|
+
return undefined;
|
|
391
|
+
if (isLessThanThreshold(percentage)) {
|
|
392
|
+
return PERCENTAGE_THRESHOLD_STRING;
|
|
393
|
+
}
|
|
394
|
+
return `${percentage}%`;
|
|
395
|
+
};
|
|
396
|
+
const formatPercentage = (percentage, decimals = DEFAULT_DECIMALS) => {
|
|
397
|
+
if (isLessThanThreshold(percentage)) {
|
|
398
|
+
return PERCENTAGE_THRESHOLD_STRING;
|
|
399
|
+
}
|
|
400
|
+
return `${cleanDecimal(percentage, decimals)}%`;
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
var AnalyticInterval;
|
|
404
|
+
(function (AnalyticInterval) {
|
|
405
|
+
AnalyticInterval["DAY"] = "DAY";
|
|
406
|
+
AnalyticInterval["HOUR"] = "HOUR";
|
|
407
|
+
AnalyticInterval["MONTH"] = "MONTH";
|
|
408
|
+
AnalyticInterval["QUARTER"] = "QUARTER";
|
|
409
|
+
AnalyticInterval["WEEK"] = "WEEK";
|
|
410
|
+
AnalyticInterval["YEAR"] = "YEAR";
|
|
411
|
+
})(AnalyticInterval || (AnalyticInterval = {}));
|
|
412
|
+
function numberWithCommas(x) {
|
|
413
|
+
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
|
414
|
+
}
|
|
415
|
+
const SECONDS_IN_MINUTE = 60;
|
|
416
|
+
const SECONDS_IN_HOUR = SECONDS_IN_MINUTE * SECONDS_IN_MINUTE;
|
|
417
|
+
const getTimeDurationLabel = (valueInSeconds) => {
|
|
418
|
+
let data = valueInSeconds;
|
|
419
|
+
if (!Number.isFinite(valueInSeconds) || valueInSeconds == null) {
|
|
420
|
+
data = 0;
|
|
421
|
+
}
|
|
422
|
+
const fixedValue = cleanDecimal(data);
|
|
423
|
+
if (data >= SECONDS_IN_HOUR) {
|
|
424
|
+
const hours = Math.floor(data / SECONDS_IN_HOUR);
|
|
425
|
+
const minutes = Math.floor((data % SECONDS_IN_HOUR) / SECONDS_IN_MINUTE);
|
|
426
|
+
const seconds = Math.floor(data % SECONDS_IN_MINUTE);
|
|
427
|
+
return `${hours}h ${minutes}m ${seconds}s`;
|
|
428
|
+
}
|
|
429
|
+
else if (data >= SECONDS_IN_MINUTE) {
|
|
430
|
+
const minutes = Math.floor(data / SECONDS_IN_MINUTE);
|
|
431
|
+
const seconds = Math.floor(data % SECONDS_IN_MINUTE);
|
|
432
|
+
return `${minutes}m ${seconds}s`;
|
|
433
|
+
}
|
|
434
|
+
else {
|
|
435
|
+
return `${fixedValue}s`;
|
|
436
|
+
}
|
|
437
|
+
};
|
|
438
|
+
const getFormattedByInterval = (value, interval, options) => {
|
|
439
|
+
if (!value)
|
|
440
|
+
return '';
|
|
441
|
+
const optionFormat = options?.isExpandDetail
|
|
442
|
+
? {
|
|
443
|
+
formatHouse: 'MMM D, h:mm A',
|
|
444
|
+
formatDay: 'MMM D, YYYY',
|
|
445
|
+
formatMonth: 'MMM YYYY',
|
|
446
|
+
formatYear: 'MMM YYYY',
|
|
447
|
+
}
|
|
448
|
+
: {
|
|
449
|
+
formatHouse: 'h A',
|
|
450
|
+
formatDay: 'MMM D',
|
|
451
|
+
formatMonth: 'MMM YYYY',
|
|
452
|
+
formatYear: 'YYYY',
|
|
453
|
+
};
|
|
454
|
+
switch (interval) {
|
|
455
|
+
case AnalyticInterval.HOUR:
|
|
456
|
+
return dayjsTz(value).format(optionFormat.formatHouse);
|
|
457
|
+
case AnalyticInterval.DAY:
|
|
458
|
+
case AnalyticInterval.WEEK:
|
|
459
|
+
return dayjsTz(value).format(optionFormat.formatDay);
|
|
460
|
+
case AnalyticInterval.MONTH:
|
|
461
|
+
return dayjsTz(value).format(optionFormat.formatMonth);
|
|
462
|
+
case AnalyticInterval.QUARTER: {
|
|
463
|
+
const d = dayjsTz(value);
|
|
464
|
+
return `Q${d.quarter()} ${d.format('YYYY')}`;
|
|
465
|
+
}
|
|
466
|
+
case AnalyticInterval.YEAR:
|
|
467
|
+
return dayjsTz(value).format(optionFormat.formatYear);
|
|
468
|
+
}
|
|
469
|
+
return dayjsTz(value).format(optionFormat.formatMonth);
|
|
100
470
|
};
|
|
101
471
|
|
|
472
|
+
const formatAnalyticDate = (dateString) => {
|
|
473
|
+
if (!dateString)
|
|
474
|
+
return 'None';
|
|
475
|
+
const date = dayjsTz(dateString);
|
|
476
|
+
const now = dayjsTz();
|
|
477
|
+
const yesterday = dayjsTz().subtract(1, 'day');
|
|
478
|
+
const isToday = date.format('YYYY-MM-DD') === now.format('YYYY-MM-DD');
|
|
479
|
+
const isYesterday = date.format('YYYY-MM-DD') === yesterday.format('YYYY-MM-DD');
|
|
480
|
+
if (isToday) {
|
|
481
|
+
return `Today at ${date.format('HH:mm')}`;
|
|
482
|
+
}
|
|
483
|
+
if (isYesterday) {
|
|
484
|
+
return `Yesterday at ${date.format('HH:mm')}`;
|
|
485
|
+
}
|
|
486
|
+
const daysDiff = now.diff(date, 'day');
|
|
487
|
+
const isWithinWeek = daysDiff >= 0 && daysDiff < 7;
|
|
488
|
+
if (isWithinWeek) {
|
|
489
|
+
return `${date.format('dddd')} at ${date.format('HH:mm')}`;
|
|
490
|
+
}
|
|
491
|
+
return `${date.format('MMM D')} at ${date.format('hh:mm a')}`;
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
const formatAnalyticData = ({ value, formatter, getTextPrice, name, }) => {
|
|
495
|
+
const dataTypeIsObjectOrArray = value !== null && (typeof value === 'object' || Array.isArray(value));
|
|
496
|
+
if (dataTypeIsObjectOrArray)
|
|
497
|
+
return value;
|
|
498
|
+
switch (formatter) {
|
|
499
|
+
case EAnalyticDataType.INTEGER: {
|
|
500
|
+
return numberWithCommas((value ?? 0).toString());
|
|
501
|
+
}
|
|
502
|
+
case EAnalyticDataType.CURRENCY: {
|
|
503
|
+
if (!getTextPrice)
|
|
504
|
+
return `${value ?? 0}`;
|
|
505
|
+
return getTextPrice(value, false);
|
|
506
|
+
}
|
|
507
|
+
case EAnalyticDataType.DATE: {
|
|
508
|
+
return formatAnalyticDate(value);
|
|
509
|
+
}
|
|
510
|
+
case EAnalyticDataType.PERCENT: {
|
|
511
|
+
if (typeof value !== 'number')
|
|
512
|
+
return calcPercentageString(0, 1, 2) ?? '';
|
|
513
|
+
return calcPercentageString(value / 100, 1, 2) ?? '';
|
|
514
|
+
}
|
|
515
|
+
case EAnalyticDataType.DURATION: {
|
|
516
|
+
return getTimeDurationLabel(Number(value));
|
|
517
|
+
}
|
|
518
|
+
case EAnalyticDataType.STRING: {
|
|
519
|
+
const fallbackValue = name === EAnalyticColumnKey.CAMPAIGNS ? '' : NONE_VALUE;
|
|
520
|
+
return value ?? fallbackValue;
|
|
521
|
+
}
|
|
522
|
+
case EAnalyticDataType.DAY: {
|
|
523
|
+
return getFormattedByInterval(value, AnalyticInterval.DAY, { isExpandDetail: true });
|
|
524
|
+
}
|
|
525
|
+
case EAnalyticDataType.HOUR: {
|
|
526
|
+
return getFormattedByInterval(value, AnalyticInterval.HOUR, { isExpandDetail: true });
|
|
527
|
+
}
|
|
528
|
+
case EAnalyticDataType.MONTH: {
|
|
529
|
+
return getFormattedByInterval(value, AnalyticInterval.MONTH, { isExpandDetail: true });
|
|
530
|
+
}
|
|
531
|
+
case EAnalyticDataType.YEAR: {
|
|
532
|
+
return getFormattedByInterval(value, AnalyticInterval.YEAR);
|
|
533
|
+
}
|
|
534
|
+
case EAnalyticDataType.WEEK: {
|
|
535
|
+
return getFormattedByInterval(value, AnalyticInterval.WEEK, { isExpandDetail: true });
|
|
536
|
+
}
|
|
537
|
+
case EAnalyticDataType.QUARTER: {
|
|
538
|
+
return getFormattedByInterval(value, AnalyticInterval.QUARTER, {
|
|
539
|
+
isExpandDetail: true,
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
case EAnalyticDataType.OBJECT:
|
|
543
|
+
case EAnalyticDataType.ARRAY: {
|
|
544
|
+
return value;
|
|
545
|
+
}
|
|
546
|
+
default:
|
|
547
|
+
return `${value}`;
|
|
548
|
+
}
|
|
549
|
+
};
|
|
550
|
+
|
|
551
|
+
const SESSION_KEY = 'sessions';
|
|
552
|
+
const hasMetricData = (metric) => {
|
|
553
|
+
const sessions = metric?.[SESSION_KEY];
|
|
554
|
+
return typeof sessions === 'number' && sessions > 0;
|
|
555
|
+
};
|
|
556
|
+
|
|
557
|
+
const parseRawJson = (raw) => {
|
|
558
|
+
if (!raw)
|
|
559
|
+
return undefined;
|
|
560
|
+
return typeof raw === 'string' ? JSON.parse(raw) : raw;
|
|
561
|
+
};
|
|
562
|
+
const parseJsonArray = (raw) => {
|
|
563
|
+
try {
|
|
564
|
+
const parsed = parseRawJson(raw);
|
|
565
|
+
if (!Array.isArray(parsed))
|
|
566
|
+
return undefined;
|
|
567
|
+
return parsed;
|
|
568
|
+
}
|
|
569
|
+
catch {
|
|
570
|
+
return undefined;
|
|
571
|
+
}
|
|
572
|
+
};
|
|
573
|
+
const parseBreakdownItems = (raw) => parseJsonArray(raw)?.map((item) => ({ ...item, total: Number(item.total) }));
|
|
574
|
+
|
|
575
|
+
const readNumeric = (metric, key) => {
|
|
576
|
+
const raw = metric?.[key];
|
|
577
|
+
return typeof raw === 'number' ? raw : 0;
|
|
578
|
+
};
|
|
579
|
+
|
|
580
|
+
const useAnalyticData = (getTextPrice) => {
|
|
581
|
+
const formatData = ({ value, formatter, name }) => {
|
|
582
|
+
return formatAnalyticData({ value, formatter, getTextPrice, name });
|
|
583
|
+
};
|
|
584
|
+
const computeMetric = ({ metric, previousMetric, metricKey, formatter, }) => {
|
|
585
|
+
if (!hasMetricData(metric))
|
|
586
|
+
return { value: 0, change: PLACEHOLDER_VALUE$1 };
|
|
587
|
+
const currentValue = readNumeric(metric, metricKey);
|
|
588
|
+
const previousValue = readNumeric(previousMetric, metricKey);
|
|
589
|
+
const value = formatData({ value: currentValue, formatter, name: metricKey });
|
|
590
|
+
if (currentValue === 0 && previousValue !== 0)
|
|
591
|
+
return { value, change: -100 };
|
|
592
|
+
if (previousValue === 0)
|
|
593
|
+
return { value, change: PLACEHOLDER_VALUE$1 };
|
|
594
|
+
const change = ((currentValue - previousValue) / previousValue) * 100;
|
|
595
|
+
return { value, change };
|
|
596
|
+
};
|
|
597
|
+
return { formatData, computeMetric };
|
|
598
|
+
};
|
|
599
|
+
|
|
600
|
+
var GPaginationDirection;
|
|
601
|
+
(function (GPaginationDirection) {
|
|
602
|
+
GPaginationDirection["NEXT"] = "NEXT";
|
|
603
|
+
GPaginationDirection["PREV"] = "PREV";
|
|
604
|
+
})(GPaginationDirection || (GPaginationDirection = {}));
|
|
605
|
+
|
|
606
|
+
const TARGET_VISITOR = [
|
|
607
|
+
{ value: EVisitorType.NEW, label: 'New' },
|
|
608
|
+
{ value: EVisitorType.RETURNING, label: 'Returning' },
|
|
609
|
+
];
|
|
610
|
+
const TARGET_DEVICES = [
|
|
611
|
+
{ value: EDeviceType.DESKTOP, label: 'Desktop' },
|
|
612
|
+
{ value: EDeviceType.TABLET, label: 'Tablet' },
|
|
613
|
+
{ value: EDeviceType.MOBILE, label: 'Mobile' },
|
|
614
|
+
];
|
|
615
|
+
const TARGET_CHANNEL = [
|
|
616
|
+
{ value: ETrafficSourceType.DIRECT, label: 'Direct' },
|
|
617
|
+
{ value: ETrafficSourceType.EMAIL, label: 'Email' },
|
|
618
|
+
{ value: ETrafficSourceType.REFERRAL, label: 'Referral' },
|
|
619
|
+
{ value: ETrafficSourceType.ORGANIC_SOCIAL, label: 'Organic social' },
|
|
620
|
+
{ value: ETrafficSourceType.ORGANIC_SEARCH, label: 'Organic search' },
|
|
621
|
+
{ value: ETrafficSourceType.PAID_SOCIAL, label: 'Paid social' },
|
|
622
|
+
{ value: ETrafficSourceType.PAID_SEARCH, label: 'Paid search' },
|
|
623
|
+
{ value: ETrafficSourceType.SMS, label: 'SMS' },
|
|
624
|
+
];
|
|
625
|
+
|
|
102
626
|
const DEFAULT_CURRENT_PERIOD_LABEL = 'Current';
|
|
103
627
|
const DEFAULT_PREVIOUS_PERIOD_LABEL = 'Previous';
|
|
104
628
|
const CHART_MIN_HEIGHT = 228;
|
|
@@ -148,32 +672,6 @@ const GBlockCenter = ({ height, align, inlineAlign, display, ...props }) => {
|
|
|
148
672
|
}, children: jsx(Box, { ...props }) }));
|
|
149
673
|
};
|
|
150
674
|
|
|
151
|
-
const TRIM_DECIMAL_ZEROS_REGEX = /\.0+$/;
|
|
152
|
-
const DEFAULT_DECIMALS = 2;
|
|
153
|
-
const trimDecimalZeros = (number) => {
|
|
154
|
-
return `${number}`.replace(TRIM_DECIMAL_ZEROS_REGEX, '');
|
|
155
|
-
};
|
|
156
|
-
const cleanDecimal = (number, decimals = DEFAULT_DECIMALS) => {
|
|
157
|
-
return trimDecimalZeros(number.toFixed(decimals));
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Utility function to calculate percentage and format it.
|
|
162
|
-
* @param part - The part value.
|
|
163
|
-
* @param total - The total value.
|
|
164
|
-
* @param decimals - The number of decimal places to format the percentage to.
|
|
165
|
-
* @returns The formatted percentage as a string.
|
|
166
|
-
*/
|
|
167
|
-
const PERCENTAGE_THRESHOLD = 0.005;
|
|
168
|
-
const PERCENTAGE_THRESHOLD_STRING = '~0%';
|
|
169
|
-
const isLessThanThreshold = (percentage) => percentage > 0 && percentage < PERCENTAGE_THRESHOLD;
|
|
170
|
-
const formatPercentage = (percentage, decimals = DEFAULT_DECIMALS) => {
|
|
171
|
-
if (isLessThanThreshold(percentage)) {
|
|
172
|
-
return PERCENTAGE_THRESHOLD_STRING;
|
|
173
|
-
}
|
|
174
|
-
return `${cleanDecimal(percentage, decimals)}%`;
|
|
175
|
-
};
|
|
176
|
-
|
|
177
675
|
function toVal(mix) {
|
|
178
676
|
if (typeof mix === 'string') {
|
|
179
677
|
return mix;
|
|
@@ -246,7 +744,7 @@ const GTooltipCard = forwardRef((props, ref) => {
|
|
|
246
744
|
useImperativeHandle(ref, () => ({ onClose: handleMouseLeave }));
|
|
247
745
|
return (jsx(TooltipCardWrapper, { className: cls('GTooltipCard cursor-pointer', alignment && ALIGNMENT_MAP[alignment], {
|
|
248
746
|
'GTooltipCard--text-underline': textDecoration === 'underline',
|
|
249
|
-
}), onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, children: jsx(Popover, { ref: popoverRef, activator: !isHideBorder ? (jsx(Box, { borderBlockEndWidth: "025", borderStyle: "dashed", borderColor: "border-tertiary", as: wrapper, ...activatorProps, children: props.children })) : (jsx(InlineStack, { children: props.children })), activatorWrapper: wrapper, onClose: () => { }, active: isMouseEnter, preferredPosition: "below", preferredAlignment: preferredAlignment, children: tooltip && (jsx("div", { className: cls(sizeClass, { 'GTooltipCard-arrow': showArrow }), children: jsx(Box, { padding: "400", children: jsxs(BlockStack, { gap: "200", children: [jsxs(BlockStack, { gap: "100", children: [jsx(Text, { as: "span", variant: "headingSm", fontWeight: "semibold", children: tooltip.title }), jsxs(BlockStack, { gap: "
|
|
747
|
+
}), onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, children: jsx(Popover, { ref: popoverRef, activator: !isHideBorder ? (jsx(Box, { borderBlockEndWidth: "025", borderStyle: "dashed", borderColor: "border-tertiary", as: wrapper, ...activatorProps, children: props.children })) : (jsx(InlineStack, { children: props.children })), activatorWrapper: wrapper, onClose: () => { }, active: isMouseEnter, preferredPosition: "below", preferredAlignment: preferredAlignment, children: tooltip && (jsx("div", { className: cls(sizeClass, { 'GTooltipCard-arrow': showArrow }), children: jsx(Box, { padding: "400", children: jsxs(BlockStack, { gap: "200", children: [jsxs(BlockStack, { gap: "100", children: [jsx(Text, { as: "span", variant: "headingSm", fontWeight: "semibold", children: tooltip.title }), jsxs(BlockStack, { gap: "200", children: [jsx(Text, { as: "span", variant: "bodyMd", tone: "subdued", fontWeight: "medium", children: jsx("span", { dangerouslySetInnerHTML: { __html: tooltip.content } }) }), tooltip.contentList && (jsx(List, { type: "bullet", children: tooltip.contentList.map((item) => (jsx(List.Item, { children: jsx(Text, { as: "span", variant: "bodyMd", tone: "subdued", fontWeight: "medium", children: item }) }, item))) }))] })] }), tooltip.formula && (jsx("div", { className: "rounded-md font-mono", style: { fontSize: '12px' }, children: tooltip.formula }))] }) }) })) }) }));
|
|
250
748
|
});
|
|
251
749
|
GTooltipCard.displayName = 'GTooltipCard';
|
|
252
750
|
|
|
@@ -254,7 +752,12 @@ const GChartSkeleton = () => {
|
|
|
254
752
|
return jsx(GSkeletonDisplayText, { height: "188px" });
|
|
255
753
|
};
|
|
256
754
|
|
|
257
|
-
const
|
|
755
|
+
const LINE_SERIES_COLORS = {
|
|
756
|
+
comparison: SERIES_COLORS.comparison,
|
|
757
|
+
single: SERIES_COLORS.current,
|
|
758
|
+
all: [...SERIES_COLORS.all],
|
|
759
|
+
};
|
|
760
|
+
const MetricChartProvider = ({ children, minHeight = CHART_MIN_HEIGHT, seriesColors = LINE_SERIES_COLORS, }) => {
|
|
258
761
|
return (jsx(PolarisVizProvider, { themes: {
|
|
259
762
|
Light: {
|
|
260
763
|
chartContainer: {
|
|
@@ -265,27 +768,72 @@ const MetricChartProvider = ({ children, minHeight = CHART_MIN_HEIGHT }) => {
|
|
|
265
768
|
verticalOverflow: true,
|
|
266
769
|
horizontalMargin: 0,
|
|
267
770
|
},
|
|
268
|
-
seriesColors
|
|
269
|
-
comparison: SERIES_COLORS.comparison,
|
|
270
|
-
single: SERIES_COLORS.current,
|
|
271
|
-
all: [...SERIES_COLORS.all],
|
|
272
|
-
},
|
|
771
|
+
seriesColors,
|
|
273
772
|
},
|
|
274
773
|
}, children: children }));
|
|
275
774
|
};
|
|
276
775
|
|
|
277
|
-
const
|
|
278
|
-
const
|
|
776
|
+
const useFormatLineChartData = ({ metricKey, columnTypes }) => {
|
|
777
|
+
const { formatData } = useAnalyticData();
|
|
778
|
+
const formatter = metricKey ? columnTypes?.[metricKey] : undefined;
|
|
779
|
+
const formatValue = (value) => {
|
|
780
|
+
return String(formatData({ value, formatter }));
|
|
781
|
+
};
|
|
782
|
+
const yAxisOptions = {
|
|
783
|
+
labelFormatter: (value) => {
|
|
784
|
+
return formatValue(Number(value) || 0);
|
|
785
|
+
},
|
|
786
|
+
};
|
|
787
|
+
return { formatValue, yAxisOptions };
|
|
788
|
+
};
|
|
789
|
+
|
|
790
|
+
const useWindowSize = () => {
|
|
791
|
+
const [windowSize, setWindowSize] = useState(() => ({
|
|
792
|
+
width: typeof window !== 'undefined' ? window.innerWidth : 0,
|
|
793
|
+
height: typeof window !== 'undefined' ? window.innerHeight : 0,
|
|
794
|
+
}));
|
|
795
|
+
const windowWidth = useMemo(() => {
|
|
796
|
+
return {
|
|
797
|
+
xs: windowSize.width <= 768,
|
|
798
|
+
md: 768 < windowSize.width && windowSize.width <= 1024,
|
|
799
|
+
lg: windowSize.width > 1024,
|
|
800
|
+
xsDown: windowSize.width < 768,
|
|
801
|
+
'1200Down': windowSize.width < 1200,
|
|
802
|
+
'1040Down': windowSize.width < 1040,
|
|
803
|
+
};
|
|
804
|
+
}, [windowSize.width]);
|
|
805
|
+
const isMobileTabletView = !windowWidth.lg;
|
|
806
|
+
const isMobileView = windowWidth.xs;
|
|
807
|
+
useEffect(() => {
|
|
808
|
+
const windowSizeHandler = () => {
|
|
809
|
+
setWindowSize({ width: window.innerWidth, height: window.innerHeight });
|
|
810
|
+
};
|
|
811
|
+
window.addEventListener('resize', windowSizeHandler);
|
|
812
|
+
return () => {
|
|
813
|
+
window.removeEventListener('resize', windowSizeHandler);
|
|
814
|
+
};
|
|
815
|
+
}, []);
|
|
816
|
+
return { windowSize, windowWidth, isMobileTabletView, isMobileView };
|
|
817
|
+
};
|
|
818
|
+
|
|
819
|
+
const MetricChart = ({ lineChartData, isLoading, isEmptyMetricData, columnTypes, metricKey, }) => {
|
|
820
|
+
const { formatValue, yAxisOptions } = useFormatLineChartData({ metricKey, columnTypes: columnTypes || {} });
|
|
821
|
+
if (!metricKey) {
|
|
822
|
+
return jsx(Fragment, {});
|
|
823
|
+
}
|
|
279
824
|
if (isLoading) {
|
|
280
825
|
return jsx(GChartSkeleton, {});
|
|
281
826
|
}
|
|
282
827
|
if (isEmptyMetricData) {
|
|
283
828
|
return jsx(MetricChartEmpty, { title: "No data yet", description: "Data needs time to gather" });
|
|
284
829
|
}
|
|
285
|
-
return (jsx(MetricChartProvider, { children: jsx(LineChart, { data: lineChartData, yAxisOptions:
|
|
286
|
-
|
|
830
|
+
return (jsx(MetricChartProvider, { children: jsx(LineChart, { data: lineChartData, yAxisOptions: yAxisOptions, theme: "Light", tooltipOptions: {
|
|
831
|
+
titleFormatter: () => `${ANALYTICS_METRIC_TOOLTIP[metricKey]?.title ?? ''}`,
|
|
832
|
+
keyFormatter: (value) => {
|
|
833
|
+
return value;
|
|
834
|
+
},
|
|
287
835
|
renderTooltipContent(data) {
|
|
288
|
-
return jsx(MetricChartTooltip, { data: data, formatValue:
|
|
836
|
+
return jsx(MetricChartTooltip, { data: data, formatValue: formatValue });
|
|
289
837
|
},
|
|
290
838
|
}, showLegend: true }) }));
|
|
291
839
|
};
|
|
@@ -337,6 +885,17 @@ const MetricChartTooltip = ({ data, formatValue }) => {
|
|
|
337
885
|
return (jsx("div", { className: "w-fit min-w-[175px]", children: jsx(Card, { padding: '200', children: jsxs(BlockStack, { gap: '100', children: [jsx(Text, { as: "p", variant: "bodySm", fontWeight: "semibold", children: data.formatters?.titleFormatter?.(data.title || '') || data.title }), jsxs(BlockStack, { gap: '100', children: [jsxs(InlineStack, { gap: '400', align: "space-between", blockAlign: "center", children: [jsxs(InlineStack, { gap: '100', blockAlign: "center", children: [jsx("div", { className: "h-[2px] w-[12px] rounded-[10px] bg-[#4FA9EA]" }), jsx(Text, { as: "p", variant: "bodySm", fontWeight: "medium", tone: "subdued", children: currentData.tooltipKey })] }), jsxs(InlineStack, { blockAlign: "center", gap: "100", children: [jsx(Text, { as: "span", variant: "bodySm", fontWeight: "semibold", children: formatValue(currentData.value) }), jsx(MetricPercentage, { change: formatPercent() })] })] }), jsxs(InlineStack, { gap: '400', align: "space-between", blockAlign: "center", children: [jsxs(InlineStack, { gap: '100', blockAlign: "center", children: [jsx("div", { className: "w-[12px] border border-dashed border-[#A1CAE7]" }), jsx(Text, { as: "p", variant: "bodySm", tone: "subdued", fontWeight: "medium", children: previousData.tooltipKey })] }), jsxs(InlineStack, { blockAlign: "center", gap: "100", children: [jsx(Text, { as: "span", variant: "bodySm", fontWeight: "semibold", children: formatValue(previousData.value) }), jsx("div", { className: "opacity-0", children: jsx(MetricPercentage, { change: formatPercent() }) })] })] })] })] }) }) }));
|
|
338
886
|
};
|
|
339
887
|
|
|
888
|
+
const MetricInfoSkeleton = ({ isShowOneLine }) => {
|
|
889
|
+
if (isShowOneLine) {
|
|
890
|
+
return (jsx(Box, { width: "40%", children: jsx(SkeletonBodyText, { lines: 1 }) }));
|
|
891
|
+
}
|
|
892
|
+
return (jsxs(BlockStack, { gap: "200", children: [jsx(Box, { width: "60%", children: jsx(SkeletonBodyText, { lines: 1 }) }), jsx(Box, { width: "40%", children: jsx(SkeletonBodyText, { lines: 1 }) })] }));
|
|
893
|
+
};
|
|
894
|
+
|
|
895
|
+
const MetricDonutChartSkeleton = () => {
|
|
896
|
+
return (jsx(Card, { children: jsxs(BlockStack, { gap: "400", children: [jsx(MetricInfoSkeleton, { isShowOneLine: true }), jsx(GChartSkeleton, {})] }) }));
|
|
897
|
+
};
|
|
898
|
+
|
|
340
899
|
var SvgChevronRightIcon = function SvgChevronRightIcon(props) {
|
|
341
900
|
return /*#__PURE__*/React.createElement("svg", Object.assign({
|
|
342
901
|
viewBox: "0 0 20 20"
|
|
@@ -347,13 +906,6 @@ var SvgChevronRightIcon = function SvgChevronRightIcon(props) {
|
|
|
347
906
|
};
|
|
348
907
|
SvgChevronRightIcon.displayName = "ChevronRightIcon";
|
|
349
908
|
|
|
350
|
-
const MetricInfoSkeleton = ({ isShowOneLine }) => {
|
|
351
|
-
if (isShowOneLine) {
|
|
352
|
-
return (jsx(Box, { width: "40%", children: jsx(SkeletonBodyText, { lines: 1 }) }));
|
|
353
|
-
}
|
|
354
|
-
return (jsxs(BlockStack, { gap: "200", children: [jsx(Box, { width: "60%", children: jsx(SkeletonBodyText, { lines: 1 }) }), jsx(Box, { width: "40%", children: jsx(SkeletonBodyText, { lines: 1 }) })] }));
|
|
355
|
-
};
|
|
356
|
-
|
|
357
909
|
const MetricValueSummary = ({ totalValue, hideComparison }) => (jsx(BlockStack, { gap: "200", children: jsxs(InlineStack, { blockAlign: "center", gap: "200", wrap: false, children: [jsx(InlineStack, { blockAlign: "center", gap: "200", children: jsx(Text, { as: "span", variant: "headingSm", children: totalValue.value }) }), !hideComparison && jsx(MetricPercentage, { change: totalValue.change })] }) }));
|
|
358
910
|
|
|
359
911
|
const MetricInfoBlock = ({ item, isHovered, isLoading, hideComparison, titleVariant = 'headingMd', titleFontWeight, onClickTitle, }) => {
|
|
@@ -373,7 +925,7 @@ const MetricChartTab = ({ item, isActive, isLoading, hideComparison, onSelect, o
|
|
|
373
925
|
return (jsx("div", { className: "w-full cursor-pointer overflow-hidden", onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), onClick: () => onSelect(item.key), children: jsx(Box, { paddingBlock: "150", paddingInline: "300", borderRadius: "200", background: isHighlighted ? 'bg-surface-active' : undefined, children: jsx(MetricInfoBlock, { item: item, isHovered: isHovered, isLoading: isLoading, hideComparison: hideComparison, titleVariant: "headingSm", titleFontWeight: "semibold", onClickTitle: onClickTitle }) }) }));
|
|
374
926
|
};
|
|
375
927
|
|
|
376
|
-
const GSelectableMetricChartCard = ({ metricInfo, dataChart, defaultActiveTab, isLoading, isEmptyMetricData, hideComparison, currentPeriodLabel = DEFAULT_CURRENT_PERIOD_LABEL, previousPeriodLabel = DEFAULT_PREVIOUS_PERIOD_LABEL, }) => {
|
|
928
|
+
const GSelectableMetricChartCard = ({ metricInfo, dataChart, defaultActiveTab, isLoading, isEmptyMetricData, hideComparison, currentPeriodLabel = DEFAULT_CURRENT_PERIOD_LABEL, previousPeriodLabel = DEFAULT_PREVIOUS_PERIOD_LABEL, columnTypes, }) => {
|
|
377
929
|
const [activeTab, setActiveTab] = useState(defaultActiveTab);
|
|
378
930
|
const lineChartData = useMemo(() => {
|
|
379
931
|
const chartData = activeTab ? dataChart[activeTab] : undefined;
|
|
@@ -389,12 +941,76 @@ const GSelectableMetricChartCard = ({ metricInfo, dataChart, defaultActiveTab, i
|
|
|
389
941
|
gridTemplateColumns: `repeat(${metricInfo.length}, 1fr)`,
|
|
390
942
|
gap: '16px',
|
|
391
943
|
marginBottom: '16px',
|
|
392
|
-
}, children: metricInfo.map((item) => (jsx(MetricChartTab, { item: item, isActive: activeTab === item.key, isLoading: isLoading, hideComparison: hideComparison, onSelect: setActiveTab }, item.key))) }), jsx(MetricChart, { lineChartData: lineChartData, isLoading: isLoading, isEmptyMetricData: isEmptyMetricData })] }));
|
|
944
|
+
}, children: metricInfo.map((item) => (jsx(MetricChartTab, { item: item, isActive: activeTab === item.key, isLoading: isLoading, hideComparison: hideComparison, onSelect: setActiveTab }, item.key))) }), jsx(MetricChart, { lineChartData: lineChartData, isLoading: isLoading, isEmptyMetricData: isEmptyMetricData, metricKey: activeTab, columnTypes: columnTypes })] }));
|
|
945
|
+
};
|
|
946
|
+
|
|
947
|
+
const calculatePercentageChange = (current, previous) => {
|
|
948
|
+
if (current === 0 && previous === 0) {
|
|
949
|
+
return undefined;
|
|
950
|
+
}
|
|
951
|
+
if (previous === 0) {
|
|
952
|
+
return current > 0 ? '100%' : '0%';
|
|
953
|
+
}
|
|
954
|
+
const change = ((current - previous) / previous) * 100;
|
|
955
|
+
return `${Math.abs(change).toFixed(0)}%`;
|
|
956
|
+
};
|
|
957
|
+
const computeDonutData = ({ name, currentValue, prevValue, hasPreviousData, }) => ({
|
|
958
|
+
name,
|
|
959
|
+
data: [{ key: 'value', value: currentValue }],
|
|
960
|
+
metadata: {
|
|
961
|
+
trend: {
|
|
962
|
+
value: hasPreviousData ? calculatePercentageChange(currentValue, prevValue) : undefined,
|
|
963
|
+
direction: currentValue >= prevValue ? 'upward' : 'downward',
|
|
964
|
+
trend: currentValue >= prevValue ? 'positive' : 'negative',
|
|
965
|
+
},
|
|
966
|
+
},
|
|
967
|
+
});
|
|
968
|
+
const getTotalCountByType = (stats, type) => {
|
|
969
|
+
const item = stats?.find((stat) => stat?.type?.toLowerCase() === type.toLowerCase());
|
|
970
|
+
return item?.total ?? 0;
|
|
971
|
+
};
|
|
972
|
+
const buildBreakdownDonutData = ({ targets, metricKey, totalsRow, comparisonTotalsRow, sort, }) => {
|
|
973
|
+
const items = parseBreakdownItems(totalsRow?.[metricKey]);
|
|
974
|
+
const comparisonItems = parseBreakdownItems(comparisonTotalsRow?.[metricKey]);
|
|
975
|
+
const hasPreviousData = !!comparisonTotalsRow?.[metricKey];
|
|
976
|
+
const result = targets.map(({ value, label }) => computeDonutData({
|
|
977
|
+
name: label,
|
|
978
|
+
currentValue: getTotalCountByType(items, value),
|
|
979
|
+
prevValue: getTotalCountByType(comparisonItems, value),
|
|
980
|
+
hasPreviousData,
|
|
981
|
+
}));
|
|
982
|
+
const getValue = (item) => item.data[0]?.value ?? 0;
|
|
983
|
+
return sort ? [...result].sort((a, b) => getValue(b) - getValue(a)) : result;
|
|
984
|
+
};
|
|
985
|
+
|
|
986
|
+
const DONUT_CHART_MIN_HEIGHT = 294;
|
|
987
|
+
const COMPACT_MAX_WIDTH = 1500;
|
|
988
|
+
const MetricDonutChartCard = ({ label, metricKey, targets, totalsRow, comparisonTotalsRow, sort, isLoading, isEmptyMetricData, minHeight = DONUT_CHART_MIN_HEIGHT, onClick, }) => {
|
|
989
|
+
const tooltip = ANALYTICS_METRIC_TOOLTIP[metricKey];
|
|
990
|
+
const { windowWidth, windowSize } = useWindowSize();
|
|
991
|
+
const [isHovered, setIsHovered] = useState(false);
|
|
992
|
+
const data = useMemo(() => buildBreakdownDonutData({ targets, metricKey, totalsRow, comparisonTotalsRow, sort }), [targets, metricKey, totalsRow, comparisonTotalsRow, sort]);
|
|
993
|
+
if (isLoading) {
|
|
994
|
+
return jsx(MetricDonutChartSkeleton, {});
|
|
995
|
+
}
|
|
996
|
+
const formatValue = (value) => {
|
|
997
|
+
return numberWithCommas(`${value}`);
|
|
998
|
+
};
|
|
999
|
+
const handleClickTitle = (event) => {
|
|
1000
|
+
event.stopPropagation();
|
|
1001
|
+
onClick();
|
|
1002
|
+
};
|
|
1003
|
+
const isCompactView = windowWidth.lg && windowSize.width < COMPACT_MAX_WIDTH;
|
|
1004
|
+
return (jsx("div", { onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), children: jsx(Card, { children: jsxs(BlockStack, { gap: "200", children: [jsx(InlineStack, { children: jsx("div", { className: "hover:cursor-pointer hover:text-[--p-color-text-link-hover]", onClick: handleClickTitle, children: jsxs(InlineStack, { children: [jsx(GTooltipCard, { tooltip: tooltip, children: jsx(Text, { as: "h3", variant: "headingMd", children: label }) }), isHovered && jsx(Icon, { source: SvgChevronRightIcon, tone: "inherit" })] }) }) }), jsx("div", { className: cls('flex items-center justify-center', {
|
|
1005
|
+
'max-h-[250px] overflow-hidden': isCompactView,
|
|
1006
|
+
}), children: isEmptyMetricData ? (jsx(MetricChartEmpty, { title: "No data yet", description: "Data needs time to gather" })) : (jsx(MetricChartProvider, { minHeight: minHeight, seriesColors: {}, children: jsx(DonutChart, { data: data, legendPosition: "left", showLegendValues: true, showLegend: true, theme: "Light", tooltipOptions: {
|
|
1007
|
+
valueFormatter: formatValue,
|
|
1008
|
+
}, labelFormatter: formatValue }) })) })] }) }) }));
|
|
393
1009
|
};
|
|
394
1010
|
|
|
395
|
-
const SingleMetricChartCard = ({ metricInfo, lineChartData, isLoading, hideComparison, isEmptyMetricData, onClickTitle, }) => {
|
|
1011
|
+
const SingleMetricChartCard = ({ metricInfo, lineChartData, isLoading, hideComparison, columnTypes, isEmptyMetricData, onClickTitle, }) => {
|
|
396
1012
|
const [isHovered, setIsHovered] = useState(false);
|
|
397
|
-
return (jsx("div", { onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), children: jsx(Card, { children: jsxs(BlockStack, { gap: "200", children: [jsx(MetricInfoBlock, { item: metricInfo, isHovered: isHovered, isLoading: isLoading, hideComparison: hideComparison, onClickTitle: onClickTitle }), jsx(MetricChart, { lineChartData: lineChartData, isLoading: isLoading, isEmptyMetricData: isEmptyMetricData })] }) }) }));
|
|
1013
|
+
return (jsx("div", { onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), children: jsx(Card, { children: jsxs(BlockStack, { gap: "200", children: [jsx(MetricInfoBlock, { item: metricInfo, isHovered: isHovered, isLoading: isLoading, hideComparison: hideComparison, onClickTitle: onClickTitle }), jsx(MetricChart, { lineChartData: lineChartData, isLoading: isLoading, isEmptyMetricData: isEmptyMetricData, columnTypes: columnTypes, metricKey: metricInfo.key })] }) }) }));
|
|
398
1014
|
};
|
|
399
1015
|
|
|
400
|
-
export { EMetricKey, GSelectableMetricChartCard, SingleMetricChartCard };
|
|
1016
|
+
export { ANALYTICS_METRIC_TOOLTIP, CHART_MIN_HEIGHT, DEFAULT_CURRENT_PERIOD_LABEL, DEFAULT_PREVIOUS_PERIOD_LABEL, EAnalyticColumnKey, EDeviceType, EMetricKey, ETrafficSourceType, EVisitorType, GSelectableMetricChartCard, MetricDonutChartCard, PLACEHOLDER_VALUE, SERIES_COLORS, SingleMetricChartCard, TARGET_CHANNEL, TARGET_DEVICES, TARGET_VISITOR, THUMB_PRODUCT_DEFAULT, TREND_TONE };
|