@quillsql/react 1.7.4 → 1.7.6

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.
Files changed (86) hide show
  1. package/lib/AddToDashboardModal.js +2 -2
  2. package/lib/AddToDashboardModal.js.map +1 -1
  3. package/lib/Chart.js +2 -2
  4. package/lib/Chart.js.map +1 -1
  5. package/lib/Context.d.ts +2 -1
  6. package/lib/Context.js +3 -1
  7. package/lib/Context.js.map +1 -1
  8. package/lib/Dashboard.js +2 -2
  9. package/lib/Dashboard.js.map +1 -1
  10. package/lib/QuillProvider.d.ts +2 -1
  11. package/lib/QuillProvider.js +2 -2
  12. package/lib/QuillProvider.js.map +1 -1
  13. package/lib/ReportBuilder.d.ts +1 -1
  14. package/lib/ReportBuilder.js +3 -2
  15. package/lib/ReportBuilder.js.map +1 -1
  16. package/lib/SQLEditor.js +5 -5
  17. package/lib/SQLEditor.js.map +1 -1
  18. package/lib/Table.js +1 -1
  19. package/lib/Table.js.map +1 -1
  20. package/lib/components/BigModal/BigModal.js +1 -0
  21. package/lib/components/BigModal/BigModal.js.map +1 -1
  22. package/lib/components/Modal/Modal.js +1 -0
  23. package/lib/components/Modal/Modal.js.map +1 -1
  24. package/lib/hooks/useQuill.js +3 -2
  25. package/lib/hooks/useQuill.js.map +1 -1
  26. package/package.json +11 -4
  27. package/.eslintrc.json +0 -19
  28. package/.prettierrc +0 -11
  29. package/.vscode/settings.json +0 -10
  30. package/src/AddToDashboardModal.tsx +0 -1213
  31. package/src/BarList.tsx +0 -580
  32. package/src/Chart.tsx +0 -1336
  33. package/src/Context.tsx +0 -249
  34. package/src/Dashboard.tsx +0 -819
  35. package/src/DateRangePicker/Calendar.tsx +0 -442
  36. package/src/DateRangePicker/DateRangePicker.tsx +0 -261
  37. package/src/DateRangePicker/DateRangePickerButton.tsx +0 -250
  38. package/src/DateRangePicker/dateRangePickerUtils.tsx +0 -480
  39. package/src/DateRangePicker/index.ts +0 -4
  40. package/src/PieChart.tsx +0 -845
  41. package/src/QuillProvider.tsx +0 -78
  42. package/src/ReportBuilder.tsx +0 -2202
  43. package/src/SQLEditor.tsx +0 -1087
  44. package/src/Table.tsx +0 -1074
  45. package/src/TableChart.tsx +0 -428
  46. package/src/assets/ArrowDownHeadIcon.tsx +0 -11
  47. package/src/assets/ArrowDownIcon.tsx +0 -14
  48. package/src/assets/ArrowDownRightIcon.tsx +0 -14
  49. package/src/assets/ArrowLeftHeadIcon.tsx +0 -11
  50. package/src/assets/ArrowRightHeadIcon.tsx +0 -11
  51. package/src/assets/ArrowRightIcon.tsx +0 -14
  52. package/src/assets/ArrowUpHeadIcon.tsx +0 -11
  53. package/src/assets/ArrowUpIcon.tsx +0 -14
  54. package/src/assets/ArrowUpRightIcon.tsx +0 -14
  55. package/src/assets/CalendarIcon.tsx +0 -14
  56. package/src/assets/DoubleArrowLeftHeadIcon.tsx +0 -18
  57. package/src/assets/DoubleArrowRightHeadIcon.tsx +0 -20
  58. package/src/assets/ExclamationFilledIcon.tsx +0 -14
  59. package/src/assets/LoadingSpinner.tsx +0 -11
  60. package/src/assets/SearchIcon.tsx +0 -14
  61. package/src/assets/XCircleIcon.tsx +0 -14
  62. package/src/assets/index.ts +0 -16
  63. package/src/components/BigModal/BigModal.tsx +0 -108
  64. package/src/components/Dropdown/Dropdown.tsx +0 -169
  65. package/src/components/Dropdown/DropdownItem.tsx +0 -68
  66. package/src/components/Dropdown/index.ts +0 -2
  67. package/src/components/Modal/Modal.tsx +0 -132
  68. package/src/components/Modal/index.ts +0 -1
  69. package/src/components/selectUtils.ts +0 -60
  70. package/src/contexts/BaseColorContext.tsx +0 -5
  71. package/src/contexts/HoveredValueContext.tsx +0 -12
  72. package/src/contexts/RootStylesContext.tsx +0 -5
  73. package/src/contexts/SelectedValueContext.tsx +0 -13
  74. package/src/contexts/index.ts +0 -4
  75. package/src/hooks/index.ts +0 -4
  76. package/src/hooks/useInternalState.tsx +0 -18
  77. package/src/hooks/useOnClickOutside.tsx +0 -23
  78. package/src/hooks/useOnWindowResize.tsx +0 -17
  79. package/src/hooks/useQuill.ts +0 -137
  80. package/src/hooks/useSelectOnKeyDown.tsx +0 -80
  81. package/src/index.ts +0 -9
  82. package/src/lib/font.ts +0 -14
  83. package/src/lib/index.ts +0 -3
  84. package/src/lib/inputTypes.ts +0 -81
  85. package/src/lib/utils.tsx +0 -46
  86. package/tsconfig.json +0 -22
package/src/Chart.tsx DELETED
@@ -1,1336 +0,0 @@
1
- /* eslint-disable @typescript-eslint/ban-ts-comment */
2
- // @ts-nocheck
3
- import React, { useState, useEffect, useContext, useMemo } from 'react';
4
- import {
5
- Area,
6
- CartesianGrid,
7
- ComposedChart as ReChartsAreaChart,
8
- ResponsiveContainer,
9
- Tooltip,
10
- XAxis,
11
- YAxis,
12
- Bar,
13
- BarChart as ReChartsBarChart,
14
- } from 'recharts';
15
- import { endOfWeek, format, isValid, startOfWeek } from 'date-fns';
16
- import { utcToZonedTime } from 'date-fns-tz';
17
- import BarList from './BarList';
18
- import PieChart, { findComplementaryAndAnalogousColors } from './PieChart';
19
- import axios from 'axios';
20
- import {
21
- ClientContext,
22
- DashboardContext,
23
- ThemeContext,
24
- DashboardFiltersContext,
25
- } from './Context';
26
- import TableChart from './TableChart';
27
- import { QuillTheme } from './QuillProvider';
28
-
29
- const colorValues = [
30
- 'slate',
31
- 'gray',
32
- 'zinc',
33
- 'neutral',
34
- 'stone',
35
- 'red',
36
- 'orange',
37
- 'amber',
38
- 'yellow',
39
- 'lime',
40
- 'green',
41
- 'emerald',
42
- 'teal',
43
- 'cyan',
44
- 'sky',
45
- 'blue',
46
- 'indigo',
47
- 'violet',
48
- 'purple',
49
- 'fuchsia',
50
- 'pink',
51
- 'rose',
52
- ] as const;
53
-
54
- export type Color = (typeof colorValues)[number];
55
-
56
- interface ButtonProps {
57
- text: string;
58
- onClick?: () => void;
59
- className?: string;
60
- }
61
-
62
- // const data = [
63
- // {
64
- // date: 'Jan 22',
65
- // SemiAnalysis: 2890,
66
- // 'The Pragmatic Engineer': 2338,
67
- // },
68
- // {
69
- // date: 'Feb 22',
70
- // SemiAnalysis: 2756,
71
- // 'The Pragmatic Engineer': 2103,
72
- // },
73
- // {
74
- // date: 'Mar 22',
75
- // SemiAnalysis: 3322,
76
- // 'The Pragmatic Engineer': 2194,
77
- // },
78
- // {
79
- // date: 'Apr 22',
80
- // SemiAnalysis: 3470,
81
- // 'The Pragmatic Engineer': 2108,
82
- // },
83
- // {
84
- // date: 'May 22',
85
- // SemiAnalysis: 3475,
86
- // 'The Pragmatic Engineer': 1812,
87
- // },
88
- // {
89
- // date: 'Jun 22',
90
- // SemiAnalysis: 3129,
91
- // 'The Pragmatic Engineer': 1726,
92
- // },
93
- // ];
94
-
95
- export const valueFormatter = ({ value, field, fields }) => {
96
- if (
97
- value === undefined ||
98
- value === null ||
99
- field === undefined ||
100
- field === null
101
- ) {
102
- return '';
103
- }
104
-
105
- const fieldInfo = fields.find(f => f.field === field);
106
-
107
- if (!fieldInfo) {
108
- return '';
109
- }
110
-
111
- const formatType = fieldInfo.format;
112
- const numValue = Number(value);
113
- const absNumber = isNaN(numValue) ? 0 : Math.abs(numValue);
114
-
115
- if (formatType === 'percent') {
116
- if (absNumber < 1) {
117
- return (absNumber * 100).toFixed(0) + '%';
118
- } else {
119
- return absNumber.toFixed(0) + '%';
120
- }
121
- }
122
-
123
- if (formatType === 'dollar_amount') {
124
- const formatter = new Intl.NumberFormat('en-US', {
125
- style: 'currency',
126
- currency: 'USD',
127
- minimumFractionDigits: 0,
128
- maximumFractionDigits: 0,
129
- });
130
-
131
- if (absNumber >= 1000000) {
132
- return formatter.format(numValue / 1000000) + 'M';
133
- } else if (absNumber >= 1000) {
134
- return formatter.format(numValue / 1000) + 'K';
135
- } else {
136
- return formatter.format(numValue);
137
- }
138
- }
139
-
140
- if (formatType === 'whole_number') {
141
- return Math.round(numValue).toString();
142
- }
143
-
144
- if (formatType === 'one_decimal_place') {
145
- return numValue.toFixed(1);
146
- }
147
-
148
- if (
149
- formatType === 'MMM_yyyy' ||
150
- formatType === 'MMM_dd_yyyy' ||
151
- formatType === 'hh_ap_pm' ||
152
- formatType === 'MMM_dd-MMM_dd' ||
153
- formatType === 'MMM_dd_hh:mm_ap_pm'
154
- ) {
155
- const dateValue = new Date(value);
156
- const utcDate = utcToZonedTime(dateValue, 'UTC');
157
- if (!isValid(utcDate)) {
158
- return 'Invalid date';
159
- }
160
- if (formatType === 'MMM_yyyy') {
161
- return format(utcDate, 'MMM yyyy');
162
- }
163
- if (formatType === 'MMM_dd_yyyy') {
164
- return format(utcDate, 'dd MMM yyyy');
165
- }
166
- if (formatType === 'hh_ap_pm') {
167
- return format(utcDate, 'hh:mm aa');
168
- }
169
- if (formatType === 'MMM_dd-MMM_dd') {
170
- const start = startOfWeek(utcDate, { weekStartsOn: 1 }); // Monday
171
- const end = endOfWeek(utcDate, { weekStartsOn: 1 }); // Sunday
172
- let formattedDate = '';
173
- // Check if start and end are in the same month
174
- if (format(start, 'MMM') === format(end, 'MMM')) {
175
- formattedDate = `${format(start, 'MMM dd')} - ${format(end, 'dd')}`;
176
- } else {
177
- formattedDate = `${format(start, 'MMM dd')} - ${format(end, 'MMM dd')}`;
178
- }
179
- return formattedDate;
180
- }
181
- if (formatType === 'MMM_dd_hh:mm_ap_pm') {
182
- const formatStr =
183
- utcDate.getMinutes() === 0 ? 'MMM do h a' : 'MMM do h:mm a';
184
- let formattedTime = format(utcDate, formatStr);
185
- formattedTime =
186
- formattedTime.slice(0, -2) + formattedTime.slice(-2).toLowerCase();
187
- return formattedTime;
188
- }
189
- }
190
-
191
- if (formatType === 'string') {
192
- return value.toString();
193
- }
194
-
195
- return value.toString();
196
- };
197
-
198
- const yAxisFields = [
199
- { field: 'avg_days', label: 'average days' },
200
- { field: 'median_days', label: 'median days' },
201
- ];
202
-
203
- const labelFormatter = (name: string) => {
204
- // return yAxisFields.filter(elem => elem.field === name)[0].label;
205
- return name;
206
- };
207
-
208
- export const ChartTooltipFrame = ({
209
- children,
210
- theme,
211
- }: {
212
- children: React.ReactNode;
213
- }) => (
214
- <div
215
- style={{
216
- borderStyle: 'solid',
217
- borderColor: theme?.borderColor || '#E5E7EB',
218
- overflow: 'hidden',
219
- borderWidth: 1,
220
- background: theme?.backgroundColor || '#ffffff',
221
- borderRadius: '6px',
222
- boxShadow:
223
- '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)',
224
- }}
225
- // className="qq-bg-white qq-text-sm qq-rounded-md qq-shadow-lg"
226
- // className={twMerge(
227
- // 'bg-white',
228
- // 'font-normal',
229
- // 'border-[12px]',
230
- // 'border-[1px]',
231
- // 'text-[#212121]'
232
- // // boxShadow.lg
233
- // )}
234
- >
235
- {children}
236
- </div>
237
- );
238
-
239
- export interface ChartTooltipRowProps {
240
- value: string;
241
- name: string;
242
- color: Color;
243
- thee: any;
244
- }
245
-
246
- export const ChartTooltipRow = ({
247
- value,
248
- name,
249
- color,
250
- theme,
251
- }: ChartTooltipRowProps) => (
252
- <div
253
- style={{
254
- marginRight: '24px',
255
- display: 'flex',
256
- alignItems: 'center',
257
- justifyContent: 'space-between',
258
- width: '100%',
259
- paddingTop: 2,
260
- paddingBottom: 2,
261
- }}
262
- // className="qq-flex qq-items-center qq-justify-between qq-space-x-8"
263
- >
264
- <div
265
- // className="qq-flex qq-items-center qq-space-x-2"
266
- style={{
267
- // marginLeft: '0.5rem',
268
- display: 'flex',
269
- alignItems: 'center',
270
- }}
271
- >
272
- <span
273
- style={{
274
- background: color,
275
- borderWidth: 2,
276
- borderColor: 'white',
277
- borderStyle: 'solid',
278
- borderColor: 'white',
279
- boxShadow: '0 1px 2px 0 rgba(0, 0, 0, 0.05)',
280
- height: '8px',
281
- width: '8px',
282
- borderRadius: 100,
283
- marginRight: 4,
284
- }}
285
- // className={twMerge(
286
- // 'shrink-0',
287
- // 'bg-black',
288
- // 'bg-white',
289
- // 'border-black',
290
- // sizing.sm.height,
291
- // sizing.sm.width,
292
- // 'qq-h-3',
293
- // 'qq-w-3',
294
- // 'qq-shadow',
295
- // 'qq-rounded-full'
296
- // border.md.all,
297
- // boxShadow.md
298
- // )}
299
- />
300
- <p
301
- // className={twMerge(
302
- // 'font-medium tabular-nums text-right whitespace-nowrap',
303
- // 'text-[#212121] !important'
304
- // )}
305
- style={{
306
- marginTop: 0,
307
- marginBottom: 0,
308
- fontFamily: theme?.fontFamily,
309
- color: theme?.primaryTextColor,
310
- fontSize: theme?.fontSizeSmall || '14px',
311
- fontWeight: theme?.fontWeightMedium || '500',
312
- }}
313
- // className="qq-font-medium qq-tabular-nums qq-text-right qq-whitespace-nowrap qq-text-black"
314
- >
315
- {value}
316
- </p>
317
- </div>
318
- <p
319
- style={{
320
- marginTop: 0,
321
- marginBottom: 0,
322
- fontFamily: theme?.fontFamily,
323
- color: theme?.secondaryTextColor,
324
- textAlign: 'right',
325
- whiteSpace: 'nowrap',
326
- overflow: 'hidden',
327
- textOverflow: 'ellipsis',
328
- fontSize: theme?.fontSizeSmall || '14px',
329
- fontWeight: theme?.fontWeightNormal || '400',
330
- }}
331
- // className={twMerge(
332
- // 'qq-text-right qq-whitespace-nowrap',
333
- // getColorClassNames(DEFAULT_COLOR, colorPalette.text).textColor,
334
- // 'qq-text-gray-500'
335
- // fontWeight.sm
336
- // )}
337
- >
338
- {name}
339
- </p>
340
- </div>
341
- );
342
-
343
- export interface ChartTooltipProps {
344
- active: boolean | undefined;
345
- payload: any;
346
- label: string;
347
- colors: string[];
348
- valueFormatter: any;
349
- theme: any;
350
- }
351
-
352
- const ChartTooltip = ({
353
- active,
354
- payload,
355
- label,
356
- colors,
357
- valueFormatter,
358
- dateFormatter,
359
- theme,
360
- }: ChartTooltipProps) => {
361
- if (active && payload) {
362
- return (
363
- <ChartTooltipFrame theme={theme}>
364
- <div
365
- style={{
366
- borderStyle: 'solid',
367
- borderBottomColor: theme?.borderColor || '#E5E7EB',
368
- background: theme?.backgroundColor || 'white',
369
- borderTop: 'none',
370
- borderLeft: 'none',
371
- borderRight: 'none',
372
- borderBottomWidth: 1,
373
- display: 'flex',
374
- flexDirection: 'column',
375
- paddingLeft: '16px',
376
- paddingRight: '16px',
377
- paddingTop: '8px',
378
- paddingBottom: '8px',
379
- }}
380
- // className="qq-flex qq-flex-col qq-py-2 qq-px-4 qq-gray-200"
381
- // className={
382
- // twMerge()
383
- // getColorClassNames(DEFAULT_COLOR, colorPalette.lightBorder)
384
- // .borderColor,
385
- // spacing.twoXl.paddingX,
386
- // spacing.sm.paddingY,
387
- // border.sm.bottom
388
- // }
389
- >
390
- <p
391
- style={{
392
- textAlign: 'left',
393
- marginTop: 0,
394
- marginBottom: 0,
395
- fontFamily: theme?.fontFamily,
396
- color: theme?.primaryTextColor,
397
- fontSize: theme?.fontSizeSmall || '14px',
398
- fontWeight: theme?.boldFontWeight || '500',
399
- paddingTop: 2,
400
- paddingBottom: 2,
401
- }}
402
- // className={twMerge(
403
- // 'qq-text-elem',
404
- // 'qq-text-black',
405
- // 'qq-font-medium',
406
- // 'qq-gray-700'
407
- // getColorClassNames(DEFAULT_COLOR, colorPalette.darkText)
408
- // .textColor,
409
- // fontWeight.md
410
- // )}
411
- >
412
- {!isNaN(new Date(label)) && dateFormatter
413
- ? dateFormatter(label)
414
- : !isNaN(new Date(label))
415
- ? format(new Date(label), 'MMM yyyy')
416
- : label}
417
- {/* {label} */}
418
- </p>
419
- </div>
420
-
421
- <div
422
- // className="qq-px-4 qq-space-y-1 qq-py-2 qq-px-4"
423
- style={{
424
- paddingRight: '16px',
425
- paddingLeft: '16px',
426
- paddingTop: '8px',
427
- paddingBottom: '8px',
428
- // marginTop: '0.25rem',
429
- }}
430
- // className={
431
- // twMerge()
432
- // spacing.twoXl.paddingX,
433
- // spacing.sm.paddingY,
434
- // 'space-y-1'
435
- // }
436
- >
437
- {payload.map(
438
- ({ value, name }: { value: number; name: string }, idx: number) => (
439
- <ChartTooltipRow
440
- key={`id-${idx}`}
441
- value={valueFormatter(value)}
442
- name={labelFormatter(name)}
443
- // color={categoryColors.get(name) ?? BaseColors.Blue}
444
- color={colors[idx]}
445
- theme={theme}
446
- />
447
- )
448
- )}
449
- </div>
450
- </ChartTooltipFrame>
451
- );
452
- }
453
- return null;
454
- };
455
-
456
- interface ChartProps {
457
- colors: string[];
458
- config?: any;
459
- chartId?: string;
460
- containerStyle?: React.CSSProperties;
461
- dashboard: any;
462
- dispatch: any;
463
- client: any;
464
- dateFilter?: any;
465
- theme: any;
466
- isDateFilter?: boolean;
467
- query?: string;
468
- }
469
-
470
- // @ts-ignore
471
- function sumByKey(arr, key) {
472
- // @ts-ignore
473
- return arr.reduce((acc, cur) => {
474
- const val = parseInt(cur[key], 10);
475
- return isNaN(val) ? acc : acc + val;
476
- }, 0);
477
- }
478
-
479
- const Chart = ({
480
- chartId,
481
- config,
482
- colors,
483
- containerStyle,
484
- dateFilter,
485
- query,
486
- }: {
487
- chartId: string;
488
- config?: any;
489
- colors?: string[];
490
- containerStyle?: React.CSSProperties;
491
- dateFilter?: boolean;
492
- query?: string;
493
- }) => {
494
- const { dispatch, dashboard } = useContext(DashboardContext);
495
- const { dashboardFilters } = useContext(DashboardFiltersContext);
496
- const [client, _] = useContext(ClientContext);
497
- const [theme] =
498
- useContext<[QuillTheme, (theme: QuillTheme) => void]>(ThemeContext);
499
- const chartColors = useMemo(() => {
500
- return colors?.length ? colors : ['#6269E9', '#E14F62'];
501
- }, [colors]);
502
- return (
503
- <ChartUpdater
504
- config={config}
505
- dispatch={dispatch}
506
- dashboard={dashboard}
507
- chartId={chartId}
508
- containerStyle={containerStyle}
509
- colors={chartColors}
510
- client={client}
511
- theme={theme}
512
- isDateFilter={dateFilter}
513
- dashboardFilters={dashboardFilters}
514
- query={query}
515
- />
516
- );
517
- };
518
-
519
- const ChartUpdater: React.FC<ChartProps> = ({
520
- colors,
521
- chartId,
522
- config,
523
- containerStyle,
524
- dashboard,
525
- dispatch,
526
- client,
527
- theme,
528
- isDateFilter,
529
- dashboardFilters,
530
- query,
531
- }) => {
532
- const [loading, setLoading] = useState(true);
533
- // console.log('chartConfig:', chartConfig);
534
- // console.log('chartConfig.rows:', chartConfig.rows);
535
- // console.log('chartConfig.xAxisField:', chartConfig.xAxisField);
536
-
537
- useEffect(() => {
538
- if (isDateFilter && !dateFilter) {
539
- return;
540
- }
541
-
542
- let isSubscribed = true;
543
- async function getChartOptions() {
544
- if (isSubscribed) {
545
- const {
546
- publicKey,
547
- customerId,
548
- environment,
549
- queryEndpoint,
550
- queryHeaders,
551
- } = client;
552
- //queryEndpoint = undefined;
553
- setLoading(true);
554
- try {
555
- // if self-hosting option, only get metadata and query
556
- if (queryEndpoint) {
557
- // const resp = await axios.get(
558
- // 'https://quill-344421.uc.r.appspot.com/selfhostitem',
559
- // {
560
- // params: {
561
- // id: chartId,
562
- // orgId: customerId,
563
- // publicKey: publicKey,
564
- // },
565
- // environment: environment || undefined,
566
- // }
567
- // );
568
- if (dashboardFilters['date_range'] && dashboard[chartId]) {
569
- const dateFilter = dashboardFilters['date_range'];
570
- dateFilter.dateField = dashboard[chartId].dateField;
571
- }
572
- const resp = await axios.post(
573
- queryEndpoint,
574
- {
575
- metadata: {
576
- id: chartId,
577
- task: 'item',
578
- filters: {
579
- ...Object.entries(dashboardFilters)
580
- .filter(([key]) => key !== 'date_range')
581
- .map(([, value]) => value),
582
- dateFilter,
583
- },
584
- },
585
- },
586
- { headers: queryHeaders }
587
- );
588
-
589
- if (resp.data) {
590
- setLoading(false);
591
- dispatch({
592
- type: 'UPDATE_DASHBOARD_ITEM',
593
- id: chartId,
594
- data: {
595
- ...resp.data,
596
- },
597
- });
598
- }
599
- } else {
600
- const resp = await axios.get(
601
- 'https://quill-344421.uc.r.appspot.com/item',
602
- {
603
- params: {
604
- id: chartId,
605
- orgId: customerId,
606
- publicKey: publicKey,
607
- filters: Object.keys(dashboardFilters).map(
608
- key => dashboardFilters[key]
609
- ),
610
- // startDate:
611
- // dateFilter &&
612
- // dateFilter.startDate &&
613
- // dateFilter.startDate.toISOString(),
614
- // endDate:
615
- // dateFilter &&
616
- // dateFilter.endDate &&
617
- // dateFilter.endDate.toISOString(),
618
- },
619
- headers: {
620
- environment: environment || undefined,
621
- },
622
- }
623
- );
624
- // console.log('RESP: ', resp.data);
625
- if (resp.data) {
626
- setLoading(false);
627
- dispatch({
628
- type: 'UPDATE_DASHBOARD_ITEM',
629
- id: chartId,
630
- data: { ...resp.data },
631
- });
632
- }
633
- }
634
- } catch (e) {
635
- console.log('Error fetching chart: ', e);
636
- setLoading(false);
637
- }
638
- }
639
- }
640
- if (config) {
641
- setLoading(false);
642
- return;
643
- }
644
-
645
- getChartOptions(); //when wouldn't you run this????? saw if statements before that implied you might not run this sometimes
646
-
647
- return () => {
648
- isSubscribed = false;
649
- };
650
- }, [dashboardFilters]);
651
-
652
- // console.log('DASHBOARD FILTERS: ', dashboardFilters);
653
-
654
- useEffect(() => {
655
- if (config) {
656
- dispatch({
657
- type: 'UPDATE_DASHBOARD_ITEM',
658
- id: chartId,
659
- data: { ...config },
660
- });
661
- }
662
- }, [config]);
663
-
664
- const dateFilter = Object.values(dashboardFilters).find(
665
- filter => filter && filter.filterType === 'DATE_RANGE'
666
- );
667
-
668
- if (!dashboard[chartId] || loading) {
669
- return (
670
- <div
671
- // className="flex flex-col flex-1 h-[100%]"
672
- style={{
673
- ...containerStyle,
674
- marginLeft: 25,
675
- marginRight: 25,
676
- boxSizing: 'content-box',
677
- height: '100%',
678
- display: 'flex',
679
- flexDirection: 'column',
680
- flex: 1,
681
- }}
682
- >
683
- <div
684
- style={{
685
- height: containerStyle?.height || 300,
686
- width: '100%',
687
- boxSizing: 'content-box',
688
- borderRadius: 8,
689
- overflow: 'hidden',
690
- }}
691
- >
692
- <svg
693
- width="100%"
694
- height="100%"
695
- xmlns="http://www.w3.org/2000/svg"
696
- xmlns:xlink="http://www.w3.org/1999/xlink"
697
- >
698
- <rect width="100%" height="100%" fill="#F9F9FA" />
699
-
700
- <defs fill="#F9F9FA">
701
- <linearGradient
702
- id="skeletonGradient"
703
- x1="0%"
704
- y1="0%"
705
- x2="10%"
706
- y2="0%"
707
- gradientUnits="userSpaceOnUse"
708
- >
709
- <stop offset="0%" stop-color="rgba(255,255,255,0)" />
710
- <stop offset="50%" stop-color="#FEFEFE" />
711
- <stop offset="100%" stop-color="rgba(255,255,255,0)" />
712
- <animate
713
- attributeName="x1"
714
- from="-100%"
715
- to="100%"
716
- dur="2s"
717
- repeatCount="indefinite"
718
- />
719
- <animate
720
- attributeName="x2"
721
- from="-50%"
722
- to="150%"
723
- dur="2s"
724
- repeatCount="indefinite"
725
- />
726
- </linearGradient>
727
- </defs>
728
-
729
- <rect width="50%" height="100%" fill="url(#skeletonGradient)">
730
- <animate
731
- attributeName="x"
732
- from="-100%"
733
- to="100%"
734
- dur="2s"
735
- repeatCount="indefinite"
736
- />
737
- </rect>
738
- </svg>
739
- </div>
740
- </div>
741
- );
742
- }
743
-
744
- if (dashboard[chartId]?.chartType === 'pie') {
745
- const { xAxisField, yAxisFields } = dashboard[chartId];
746
- return (
747
- <PieChart
748
- // @ts-ignore
749
- containerStyle={containerStyle}
750
- data={dashboard[chartId].rows.map(row => {
751
- return {
752
- ...row,
753
- count:
754
- parseInt(row[yAxisFields[0].field]) /
755
- sumByKey(dashboard[chartId].rows, yAxisFields[0].field),
756
- };
757
- })}
758
- category={yAxisFields[0].field}
759
- index={xAxisField}
760
- colors={colors}
761
- theme={theme}
762
- />
763
- );
764
- }
765
-
766
- if (dashboard[chartId]?.chartType === 'table') {
767
- return (
768
- <TableChart
769
- data={dashboard[chartId].rows}
770
- yAxisFields={dashboard[chartId].yAxisFields}
771
- theme={theme}
772
- containerStyle={containerStyle}
773
- />
774
- );
775
- }
776
-
777
- if (dashboard[chartId]?.chartType === 'bar') {
778
- return (
779
- <BarList
780
- data={dashboard[chartId].rows}
781
- theme={theme}
782
- yAxisFields={dashboard[chartId].yAxisFields}
783
- xAxisFormat={dashboard[chartId].xAxisFormat}
784
- colors={colors}
785
- xAxisField={dashboard[chartId].xAxisField}
786
- containerStyle={containerStyle}
787
- />
788
- );
789
- }
790
-
791
- if (dashboard[chartId]?.chartType === 'column') {
792
- return (
793
- <BarChart
794
- colors={colors}
795
- theme={theme}
796
- yAxisFields={dashboard[chartId].yAxisFields}
797
- // @ts-ignore
798
- data={dashboard[chartId].rows}
799
- xAxisField={dashboard[chartId].xAxisField}
800
- xAxisLabel={dashboard[chartId].xAxisLabel}
801
- xAxisFormat={dashboard[chartId].xAxisFormat}
802
- containerStyle={containerStyle}
803
- />
804
- );
805
- }
806
-
807
- if (dashboard[chartId]?.chartType === 'metric') {
808
- return (
809
- <div
810
- style={{
811
- fontFamily: theme?.fontFamily,
812
- fontSize: 32,
813
- color: theme?.primaryTextColor,
814
- fontWeight: '600',
815
- textOverflow: 'ellipsis',
816
- margin: 0,
817
- paddingTop: 20,
818
- paddingBottom: 20,
819
- paddingLeft: 25,
820
- paddingRight: 25,
821
- whiteSpace: 'nowrap',
822
- boxSizing: 'content-box',
823
- display: 'block',
824
- maxWidth: '100%',
825
- textAlign: 'left',
826
- overflow: 'hidden',
827
- height: containerStyle?.height || '100%',
828
- display: 'flex',
829
- flexDirection: 'column',
830
- // background: 'red',
831
- }}
832
- // className="qq-flex qq-flex-col qq-text-xl"
833
- >
834
- {dashboard[chartId].rows.length > 0 &&
835
- valueFormatter({
836
- value: dashboard[chartId].rows[0][dashboard[chartId].xAxisField],
837
- field: dashboard[chartId].xAxisField,
838
- fields: [
839
- {
840
- field: dashboard[chartId].xAxisField,
841
- format: dashboard[chartId].xAxisFormat,
842
- },
843
- ],
844
- })}
845
- </div>
846
- );
847
- }
848
-
849
- return (
850
- <LineChart
851
- colors={colors}
852
- yAxisFields={dashboard[chartId].yAxisFields}
853
- // @ts-ignore
854
- data={dashboard[chartId].rows}
855
- xAxisField={dashboard[chartId].xAxisField}
856
- xAxisLabel={dashboard[chartId].xAxisLabel}
857
- xAxisFormat={dashboard[chartId].xAxisFormat}
858
- containerStyle={containerStyle}
859
- theme={theme}
860
- />
861
- );
862
- };
863
-
864
- function formatNumber(num, label) {
865
- num = Number(num);
866
- if (num < 1000) {
867
- return num.toFixed(2) + ' ' + label;
868
- }
869
- if (num >= 1.0e6) {
870
- return Math.round(num / 1.0e6).toLocaleString() + 'M';
871
- } else {
872
- return Math.round(num / 1.0e6).toLocaleString() + 'M';
873
- }
874
- }
875
-
876
- // @ts-ignore
877
- // function getDomain(data, fields) {
878
- // // @ts-ignore
879
- // const fieldsArray = fields.map(elem => elem.field);
880
- // const [minValue, maxValue] = data.reduce(
881
- // // @ts-ignore
882
- // ([min, max], item) => [
883
- // // @ts-ignore
884
- // Math.min(min, ...fieldsArray.map(field => item[field])),
885
- // // @ts-ignore
886
- // Math.max(max, ...fieldsArray.map(field => item[field])),
887
- // ],
888
- // [Infinity, -Infinity]
889
- // );
890
-
891
- // const adjustedMin = Math.min(minValue, 0);
892
- // const padding = Math.round(0.2 * (maxValue - minValue));
893
- // const adjustedMax = maxValue + padding;
894
-
895
- // return [adjustedMin, adjustedMax];
896
- // }
897
- function getDomain(data, fields) {
898
- const fieldsArray = fields.map(elem => elem.field);
899
- const numericValues = [];
900
-
901
- data.forEach(item => {
902
- fieldsArray.forEach(field => {
903
- let value = item[field];
904
- // Try to cast the value to a number
905
- const numericValue = parseFloat(value);
906
-
907
- if (!isNaN(numericValue)) {
908
- value = numericValue;
909
- }
910
-
911
- if (typeof value === 'number') {
912
- numericValues.push(value);
913
- }
914
- });
915
- });
916
-
917
- const [minValue, maxValue] = numericValues.reduce(
918
- ([min, max], value) => [Math.min(min, value), Math.max(max, value)],
919
- [Infinity, -Infinity]
920
- );
921
-
922
- const adjustedMin = Math.min(minValue, 0);
923
- const padding = Math.round(0.2 * (maxValue - minValue));
924
- const adjustedMax = maxValue + padding;
925
-
926
- return [adjustedMin, adjustedMax];
927
- }
928
-
929
- const CustomizedTick = ({
930
- x,
931
- y,
932
- payload,
933
- theme,
934
- }: {
935
- x: number;
936
- y: number;
937
- payload: any;
938
- theme: any;
939
- }) => {
940
- // const { x, y, payload } = props;
941
- const maxLength = 10;
942
- const value = payload.value;
943
- const truncatedValue =
944
- value.length > maxLength ? value.substring(0, maxLength) + '...' : value;
945
- return (
946
- <text
947
- xlinkTitle={value}
948
- fill={theme?.chartLabelColor}
949
- fontSize={12}
950
- x={x}
951
- y={y + 15}
952
- width={30}
953
- textAnchor="middle"
954
- >
955
- {truncatedValue}
956
- </text>
957
- );
958
- };
959
-
960
- function BarChart({
961
- colors,
962
- yAxisFields,
963
- data,
964
- containerStyle,
965
- xAxisField,
966
- xAxisLabel,
967
- xAxisFormat,
968
- theme,
969
- }: {
970
- colors: string[];
971
- yAxisFields: any[];
972
- data: any[];
973
- containerStyle?: React.CSSProperties;
974
- xAxisField: string;
975
- xAxisLabel: string;
976
- xAxisFormat: string;
977
- theme: any;
978
- }) {
979
- const newColors = findComplementaryAndAnalogousColors(colors[0], colors[1]);
980
- return (
981
- <div
982
- style={{
983
- ...containerStyle,
984
- boxSizing: 'content-box',
985
- // display: 'flex',
986
- // flexDirection: 'column',
987
- // flex: 1,
988
- // height: '100%',
989
- // minHeight: 400,
990
- // minWidth: 400,
991
- // height: 400,
992
- // width: 800,
993
- // width: '100%',
994
- }}
995
- // className="qq-flex qq-flex-col qq-flex-1 qq-h-full qq-w-full"
996
- >
997
- <ResponsiveContainer width="100%" height={'100%'}>
998
- <ReChartsBarChart
999
- data={data}
1000
- // stackOffset={'none'}
1001
- layout={'horizontal'}
1002
- >
1003
- <CartesianGrid
1004
- strokeDasharray="3 3"
1005
- horizontal={true}
1006
- vertical={false}
1007
- />
1008
-
1009
- <YAxis
1010
- // width={56}
1011
- // width={30}
1012
- // textAnchor="start"
1013
- hide={false}
1014
- axisLine={false}
1015
- tickLine={false}
1016
- type="number"
1017
- domain={getDomain(data, yAxisFields)}
1018
- // domain={[0, 150]}
1019
- tick={{ transform: 'translate(-3, 0)' }}
1020
- style={{
1021
- fontSize: '12px',
1022
- fontFamily: theme.chartLabelFontFamily,
1023
- }}
1024
- tickFormatter={tick =>
1025
- valueFormatter({
1026
- value: tick,
1027
- field: yAxisFields[0].field || 'what',
1028
- fields: yAxisFields,
1029
- })
1030
- }
1031
- // tickFormatter={valueFormatter}
1032
- />
1033
- <XAxis
1034
- hide={false}
1035
- dataKey={xAxisField}
1036
- // interval="preserveStartEnd"
1037
- tick={{ transform: 'translate(0, 6)' }}
1038
- //padding between labels and axis
1039
- style={{
1040
- fontSize: '12px',
1041
- // TODO: generalize
1042
- fontFamily: theme.chartLabelFontFamily,
1043
- marginTop: '20px',
1044
- }}
1045
- tickLine={false}
1046
- axisLine={false}
1047
- tickFormatter={tick =>
1048
- valueFormatter({
1049
- value: tick,
1050
- field: xAxisField,
1051
- fields: [{ field: xAxisField, format: xAxisFormat }],
1052
- })
1053
- }
1054
- // tickFormatter={tick => valueFormatter(tick, xAxisFormat)}
1055
- />
1056
- {/* <XAxis
1057
- hide={false}
1058
- dataKey={xAxisField}
1059
- interval="preserveStartEnd"
1060
- tick={{ transform: 'translate(0, 6)' }} //padding between labels and axis
1061
- style={{
1062
- fontSize: '12px',
1063
- // TODO: generalize
1064
- fontFamily: 'Inter; Helvetica',
1065
- marginTop: '20px',
1066
- }}
1067
- tickLine={false}
1068
- axisLine={false}
1069
- />
1070
- <YAxis
1071
- width={56}
1072
- hide={false}
1073
- axisLine={false}
1074
- tickLine={false}
1075
- type="number"
1076
- // domain={getDomain(data, yAxisFields)}
1077
- domain={[0, 5]}
1078
- tick={{ transform: 'translate(-3, 0)' }}
1079
- style={{
1080
- fontSize: '12px',
1081
- fontFamily: 'Inter; Helvetica',
1082
- }}
1083
- tickFormatter={valueFormatter}
1084
- /> */}
1085
- <Tooltip
1086
- wrapperStyle={{ outline: 'none' }}
1087
- isAnimationActive={false}
1088
- cursor={{ fill: '#d1d5db', opacity: '0.15' }}
1089
- content={({ active, payload, label }) => {
1090
- return (
1091
- <ChartTooltip
1092
- theme={theme}
1093
- active={active}
1094
- payload={payload}
1095
- label={label}
1096
- dateFormatter={value =>
1097
- valueFormatter({
1098
- value,
1099
- field: xAxisField,
1100
- fields: [{ field: xAxisField, format: xAxisFormat }],
1101
- })
1102
- }
1103
- valueFormatter={value =>
1104
- valueFormatter({
1105
- value,
1106
- field: payload[0].dataKey,
1107
- fields: yAxisFields,
1108
- })
1109
- }
1110
- colors={newColors}
1111
- />
1112
- );
1113
- }}
1114
- position={{ y: 0 }}
1115
- />
1116
- {/* <Legend
1117
- verticalAlign="top"
1118
- height={100}
1119
- content={({ payload }) =>
1120
- ChartLegend({ payload }, categoryColors, setLegendHeight)
1121
- }
1122
- /> */}
1123
- {yAxisFields.map((elem, index) => (
1124
- <Bar
1125
- key={elem.field}
1126
- name={elem.label}
1127
- dataKey={elem.field}
1128
- type="linear"
1129
- // stackId={stack || relative ? "a" : undefined}
1130
- fill={newColors[index]}
1131
- // barSize={20}
1132
- isAnimationActive={true}
1133
- />
1134
- ))}
1135
- </ReChartsBarChart>
1136
- </ResponsiveContainer>
1137
- </div>
1138
- );
1139
- }
1140
-
1141
- function LineChart({
1142
- colors,
1143
- yAxisFields,
1144
- data,
1145
- containerStyle,
1146
- xAxisField,
1147
- xAxisLabel,
1148
- xAxisFormat,
1149
- theme,
1150
- }: {
1151
- colors: string[];
1152
- yAxisFields: any[];
1153
- data: any[];
1154
- containerStyle?: React.CSSProperties;
1155
- xAxisField: string;
1156
- xAxisLabel: string;
1157
- xAxisFormat: string;
1158
- theme: any;
1159
- }) {
1160
- // console.log('CONTAINER: ', containerStyle);
1161
- if (!yAxisFields || !yAxisFields.length) {
1162
- return null;
1163
- }
1164
- return (
1165
- <div
1166
- style={{ ...containerStyle, boxSizing: 'content-box' }}
1167
- // className="qq-flex qq-flex-col qq-flex-1 qq-h-full qq-w-full"
1168
- >
1169
- <ResponsiveContainer width="100%" height={'100%'}>
1170
- <ReChartsAreaChart data={data}>
1171
- <CartesianGrid
1172
- strokeDasharray="3 3"
1173
- horizontal={true}
1174
- vertical={false}
1175
- />
1176
- <XAxis
1177
- // hide={!showXAxis}
1178
- dataKey={xAxisField}
1179
- tick={{ transform: 'translate(0, 6)' }}
1180
- // ticks={
1181
- // startEndOnly
1182
- // ? [data[0][index], data[data.length - 1][index]]
1183
- // : undefined
1184
- // }
1185
- style={{
1186
- fontSize: '12px',
1187
- fontFamily:
1188
- theme?.chartLabelFontFamily ||
1189
- theme?.fontFamily ||
1190
- 'Inter; Helvetica',
1191
- color: theme?.chartLabelColor || '#666666',
1192
- }}
1193
- // interval="preserveStartEnd"
1194
- interval="preserveStartEnd"
1195
- // interval={0}
1196
- tickLine={false}
1197
- axisLine={false}
1198
- padding={{ left: 10, right: 10 }}
1199
- minTickGap={5}
1200
- tickFormatter={tick =>
1201
- valueFormatter({
1202
- value: tick,
1203
- field: xAxisField,
1204
- fields: [{ field: xAxisField, format: xAxisFormat }],
1205
- })
1206
- }
1207
- // tickFormatter={tick => format(new Date(tick), 'MMM yyyy')}
1208
- />
1209
- <YAxis
1210
- // textAnchor="left"
1211
- // tickMargin={0}
1212
- // width={56}
1213
- // width={30}
1214
- // hide={!showYAxis}
1215
- axisLine={false}
1216
- tickLine={false}
1217
- type="number"
1218
- // domain={yAxisDomain as AxisDomain}
1219
- domain={getDomain(data, yAxisFields)}
1220
- tick={{ transform: 'translate(-3, 0)' }}
1221
- style={{
1222
- fontSize: '12px',
1223
- fontFamily:
1224
- theme?.chartLabelFontFamily ||
1225
- theme?.fontFamily ||
1226
- 'Inter; Helvetica',
1227
- color: theme?.chartLabelColor || '#666666',
1228
- }}
1229
- tickFormatter={value =>
1230
- valueFormatter({
1231
- value,
1232
- field: yAxisFields[0].field,
1233
- fields: [
1234
- ...yAxisFields,
1235
- { field: xAxisField, format: xAxisFormat },
1236
- ],
1237
- })
1238
- }
1239
- />
1240
- <Tooltip
1241
- wrapperStyle={{ outline: 'none' }}
1242
- isAnimationActive={false}
1243
- cursor={{ stroke: '#d1d5db', strokeWidth: 1 }}
1244
- content={({ active, payload, label }) => {
1245
- return (
1246
- <ChartTooltip
1247
- active={active}
1248
- payload={payload}
1249
- label={label}
1250
- dateFormatter={value =>
1251
- valueFormatter({
1252
- value,
1253
- field: xAxisField,
1254
- fields: [{ field: xAxisField, format: xAxisFormat }],
1255
- })
1256
- }
1257
- valueFormatter={value =>
1258
- valueFormatter({
1259
- value,
1260
- field: payload[0].dataKey,
1261
- fields: [
1262
- ...yAxisFields,
1263
- { field: xAxisField, format: xAxisFormat },
1264
- ],
1265
- })
1266
- }
1267
- colors={colors}
1268
- theme={theme}
1269
- />
1270
- );
1271
- }}
1272
- position={{ y: 0 }}
1273
- />
1274
- {/* <Legend
1275
- verticalAlign="top"
1276
- height={100}
1277
- content={({ payload }) =>
1278
- ChartLegend({ payload }, categoryColors, setLegendHeight)
1279
- }
1280
- /> */}
1281
-
1282
- {/* <defs>
1283
- <linearGradient id={'colorUj'} x1="0" y1="0" x2="0" y2="1">
1284
- <stop offset="5%" stopColor={'#E14F62'} stopOpacity={0.4} />
1285
- <stop
1286
- offset="95%"
1287
- stopColor={'rgba(0,0,0,0)'}
1288
- stopOpacity={0}
1289
- />
1290
- </linearGradient>
1291
- </defs> */}
1292
-
1293
- {yAxisFields.map((elem, index) => (
1294
- <defs key={`defs${elem.field + index}`}>
1295
- <linearGradient
1296
- id={`gradient${index}`}
1297
- x1="0"
1298
- y1="0"
1299
- x2="0"
1300
- y2="1"
1301
- >
1302
- <stop offset="5%" stopColor={colors[index]} stopOpacity={0.4} />
1303
- <stop
1304
- offset="95%"
1305
- stopColor={'rgba(0,0,0,0)'}
1306
- stopOpacity={0}
1307
- />
1308
- </linearGradient>
1309
- </defs>
1310
- ))}
1311
-
1312
- {/* {categories.map(category => {
1313
- console.log('category: ', category);
1314
- return ( */}
1315
- {yAxisFields.map((elem, index) => (
1316
- <Area
1317
- key={elem.field}
1318
- name={elem.label}
1319
- type="linear"
1320
- dataKey={elem.field}
1321
- stroke={colors[index]}
1322
- // if hide area
1323
- // fill="transparent"
1324
- fill={`url(#gradient${index})`}
1325
- strokeWidth={2}
1326
- dot={false}
1327
- isAnimationActive={true}
1328
- />
1329
- ))}
1330
- </ReChartsAreaChart>
1331
- </ResponsiveContainer>
1332
- </div>
1333
- );
1334
- }
1335
-
1336
- export default Chart;