@quillsql/react 1.7.5 → 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 (73) hide show
  1. package/lib/ReportBuilder.js +1 -0
  2. package/lib/ReportBuilder.js.map +1 -1
  3. package/lib/SQLEditor.js +3 -3
  4. package/lib/SQLEditor.js.map +1 -1
  5. package/lib/Table.js +1 -1
  6. package/lib/Table.js.map +1 -1
  7. package/lib/components/BigModal/BigModal.js +1 -0
  8. package/lib/components/BigModal/BigModal.js.map +1 -1
  9. package/lib/components/Modal/Modal.js +1 -0
  10. package/lib/components/Modal/Modal.js.map +1 -1
  11. package/lib/hooks/useQuill.js +1 -0
  12. package/lib/hooks/useQuill.js.map +1 -1
  13. package/package.json +11 -4
  14. package/.eslintrc.json +0 -19
  15. package/.prettierrc +0 -11
  16. package/.vscode/settings.json +0 -10
  17. package/src/AddToDashboardModal.tsx +0 -1220
  18. package/src/BarList.tsx +0 -580
  19. package/src/Chart.tsx +0 -1337
  20. package/src/Context.tsx +0 -252
  21. package/src/Dashboard.tsx +0 -820
  22. package/src/DateRangePicker/Calendar.tsx +0 -442
  23. package/src/DateRangePicker/DateRangePicker.tsx +0 -261
  24. package/src/DateRangePicker/DateRangePickerButton.tsx +0 -250
  25. package/src/DateRangePicker/dateRangePickerUtils.tsx +0 -480
  26. package/src/DateRangePicker/index.ts +0 -4
  27. package/src/PieChart.tsx +0 -845
  28. package/src/QuillProvider.tsx +0 -81
  29. package/src/ReportBuilder.tsx +0 -2208
  30. package/src/SQLEditor.tsx +0 -1093
  31. package/src/Table.tsx +0 -1074
  32. package/src/TableChart.tsx +0 -428
  33. package/src/assets/ArrowDownHeadIcon.tsx +0 -11
  34. package/src/assets/ArrowDownIcon.tsx +0 -14
  35. package/src/assets/ArrowDownRightIcon.tsx +0 -14
  36. package/src/assets/ArrowLeftHeadIcon.tsx +0 -11
  37. package/src/assets/ArrowRightHeadIcon.tsx +0 -11
  38. package/src/assets/ArrowRightIcon.tsx +0 -14
  39. package/src/assets/ArrowUpHeadIcon.tsx +0 -11
  40. package/src/assets/ArrowUpIcon.tsx +0 -14
  41. package/src/assets/ArrowUpRightIcon.tsx +0 -14
  42. package/src/assets/CalendarIcon.tsx +0 -14
  43. package/src/assets/DoubleArrowLeftHeadIcon.tsx +0 -18
  44. package/src/assets/DoubleArrowRightHeadIcon.tsx +0 -20
  45. package/src/assets/ExclamationFilledIcon.tsx +0 -14
  46. package/src/assets/LoadingSpinner.tsx +0 -11
  47. package/src/assets/SearchIcon.tsx +0 -14
  48. package/src/assets/XCircleIcon.tsx +0 -14
  49. package/src/assets/index.ts +0 -16
  50. package/src/components/BigModal/BigModal.tsx +0 -108
  51. package/src/components/Dropdown/Dropdown.tsx +0 -169
  52. package/src/components/Dropdown/DropdownItem.tsx +0 -68
  53. package/src/components/Dropdown/index.ts +0 -2
  54. package/src/components/Modal/Modal.tsx +0 -132
  55. package/src/components/Modal/index.ts +0 -1
  56. package/src/components/selectUtils.ts +0 -60
  57. package/src/contexts/BaseColorContext.tsx +0 -5
  58. package/src/contexts/HoveredValueContext.tsx +0 -12
  59. package/src/contexts/RootStylesContext.tsx +0 -5
  60. package/src/contexts/SelectedValueContext.tsx +0 -13
  61. package/src/contexts/index.ts +0 -4
  62. package/src/hooks/index.ts +0 -4
  63. package/src/hooks/useInternalState.tsx +0 -18
  64. package/src/hooks/useOnClickOutside.tsx +0 -23
  65. package/src/hooks/useOnWindowResize.tsx +0 -17
  66. package/src/hooks/useQuill.ts +0 -138
  67. package/src/hooks/useSelectOnKeyDown.tsx +0 -80
  68. package/src/index.ts +0 -9
  69. package/src/lib/font.ts +0 -14
  70. package/src/lib/index.ts +0 -3
  71. package/src/lib/inputTypes.ts +0 -81
  72. package/src/lib/utils.tsx +0 -46
  73. package/tsconfig.json +0 -22
@@ -1,2208 +0,0 @@
1
- import React, { useState, useContext, useCallback, useEffect } from 'react';
2
- // import './nightOwlLight.css';
3
- import axios from 'axios';
4
- import { ClientContext, SchemaContext, ThemeContext } from './Context';
5
- import {
6
- ChevronDownIcon,
7
- ChevronRightIcon,
8
- SparklesIcon,
9
- MagnifyingGlassIcon,
10
- XMarkIcon,
11
- CheckIcon,
12
- } from '@heroicons/react/20/solid';
13
- import { convertPostgresColumn } from './SQLEditor';
14
- import { format } from 'date-fns';
15
- import { QuillTheme } from './QuillProvider';
16
-
17
- interface Option {
18
- value: string;
19
- label: string;
20
- }
21
-
22
- interface SelectComponentProps {
23
- onChange: (value: string) => void;
24
- value: string;
25
- options: Option[];
26
- }
27
-
28
- interface ButtonComponentProps {
29
- onClick: () => void;
30
- label: string;
31
- }
32
-
33
- interface ModalTriggerComponentProps {
34
- onClick: () => void;
35
- label: string;
36
- }
37
-
38
- interface ModalComponentProps {
39
- children: any;
40
- isOpen: boolean;
41
- onClose: () => void;
42
- title: string;
43
- }
44
-
45
- interface TextInputComponentProps {
46
- onChange: (e: any) => void;
47
- value: string;
48
- id: string;
49
- }
50
-
51
- interface ReportBuilderProps {
52
- onChangeQuery: (query: string) => void;
53
- onChangeData: (data: object[]) => void;
54
- onChangeColumns: (columns: object[]) => void;
55
- onChangeLoading: (loading: boolean) => void;
56
- onError: (error: string) => void;
57
- SelectComponent?: (props: SelectComponentProps) => JSX.Element;
58
- ButtonComponent?: (props: ButtonComponentProps) => JSX.Element;
59
- ModalComponent?: (props: ModalComponentProps) => JSX.Element;
60
- ModalTriggerComponent?: (props: ModalTriggerComponentProps) => JSX.Element;
61
- TextInputComponent?: (props: TextInputComponentProps) => JSX.Element;
62
- tagStyle?: React.CSSProperties;
63
- }
64
-
65
- function QuillModal({ children, isOpen, onClose, title, theme }) {
66
- if (!isOpen) {
67
- return null;
68
- }
69
- return (
70
- <div
71
- style={{
72
- position: 'absolute',
73
- top: 45,
74
- minWidth: 300,
75
- left: 0,
76
- backgroundColor: theme.backgroundColor || 'white',
77
- padding: '20px',
78
- zIndex: '1000',
79
- borderRadius: 8,
80
- boxShadow: '0 1px 8px 0 rgba(0,0,0,.09)',
81
- borderWidth: theme.borderWidth,
82
- borderStyle: 'solid',
83
- borderColor: theme.borderColor,
84
- }}
85
- >
86
- {children}
87
- </div>
88
- );
89
- }
90
-
91
- export default function ReportBuilder({
92
- onChangeQuery,
93
- onChangeData,
94
- onChangeColumns,
95
- onChangeLoading,
96
- onError,
97
- SelectComponent,
98
- ButtonComponent,
99
- ModalComponent,
100
- ModalTriggerComponent,
101
- TextInputComponent,
102
- tagStyle,
103
- }: ReportBuilderProps) {
104
- const [data, setData] = useState([]);
105
- const [client] = useContext(ClientContext);
106
- const [schema, setSchema] = useContext(SchemaContext);
107
- const [theme] =
108
- useContext<[QuillTheme, (theme: QuillTheme) => void]>(ThemeContext);
109
- const [columns, setColumns] = useState([]);
110
- const [fields, setFields] = useState([]);
111
-
112
- useEffect(() => {
113
- async function getData(schema) {
114
- if (schema.length && !data.length) {
115
- const { publicKey, customerId, environment } = client;
116
- const response = await axios.post(
117
- `https://quill-344421.uc.r.appspot.com/dashquery`,
118
- {
119
- query: `select * from ${schema[0].displayName} limit 10;`,
120
- },
121
- {
122
- params: {
123
- orgId: customerId,
124
- publicKey,
125
- },
126
- headers: {
127
- Authorization: `Bearer `,
128
- environment: environment || undefined,
129
- },
130
- }
131
- );
132
- setData(response.data.rows);
133
- }
134
- }
135
- getData(schema);
136
- }, [schema]);
137
-
138
- useEffect(() => {
139
- let isSubscribed = true;
140
- async function getSchema() {
141
- const { publicKey, environment } = client;
142
- const response3 = await axios.get(
143
- `https://quill-344421.uc.r.appspot.com/schema2/${publicKey}/`,
144
- {
145
- headers: {
146
- Authorization: `Bearer `,
147
- environment: environment || undefined,
148
- },
149
- }
150
- );
151
- if (isSubscribed) {
152
- setSchema(response3.data.tables);
153
- }
154
- }
155
- if (isSubscribed) {
156
- getSchema();
157
- }
158
- return () => {
159
- isSubscribed = false;
160
- };
161
- }, []);
162
-
163
- const runQuery = async query => {
164
- const {
165
- publicKey,
166
- customerId,
167
- environment,
168
- queryEndpoint,
169
- queryHeaders,
170
- withCredentials,
171
- } = client;
172
-
173
- let response;
174
- if (queryEndpoint) {
175
- response = await axios.post(
176
- queryEndpoint,
177
- { metadata: { query, task: 'query' } },
178
- { headers: queryHeaders, withCredentials }
179
- );
180
- } else {
181
- response = await axios.post(
182
- `https://quill-344421.uc.r.appspot.com/dashquery`,
183
- {
184
- query,
185
- },
186
- {
187
- params: {
188
- orgId: customerId,
189
- publicKey,
190
- },
191
- headers: {
192
- Authorization: `Bearer `,
193
- environment: environment || undefined,
194
- },
195
- }
196
- );
197
- }
198
- if (response && response.data && response.data.errorMessage) {
199
- onError(response.data.errorMessage);
200
- setData([]);
201
- onChangeData([]);
202
- setColumns([]);
203
- onChangeColumns([]);
204
- setFields([]);
205
- return;
206
- }
207
- setData(response.data.rows);
208
- onChangeData(response.data.rows);
209
- setColumns(response.data.fields.map(elem => convertPostgresColumn(elem)));
210
- onChangeColumns(
211
- response.data.fields.map(elem => convertPostgresColumn(elem))
212
- );
213
- setFields(response.data.fields);
214
- };
215
-
216
- if (!schema.length) {
217
- return null;
218
- }
219
-
220
- return (
221
- <ReportingTool
222
- theme={theme}
223
- data={data}
224
- schema={schema}
225
- onChangeQuery={onChangeQuery}
226
- runQuery={runQuery}
227
- SelectComponent={
228
- SelectComponent
229
- ? SelectComponent
230
- : ({ onChange, value, options }: SelectComponentProps) => {
231
- return (
232
- <select
233
- onChange={event => onChange(event.target.value)}
234
- value={value}
235
- id={'reportbuilderdropdown'}
236
- style={{
237
- width: '100%',
238
- minWidth: 230,
239
- outline: 'none',
240
- textAlign: 'left',
241
- whiteSpace: 'nowrap',
242
- overflow: 'hidden',
243
- textOverflow: 'ellipsis',
244
- borderRadius: 6,
245
- paddingLeft: 12,
246
- paddingRight: 12,
247
- height: 38,
248
- borderWidth: theme.borderWidth,
249
- borderColor: theme.borderColor,
250
- background: theme.backgroundColor,
251
- color: theme.primaryTextColor,
252
- boxShadow: '0 1px 2px 0 rgba(0,0,0,.05)',
253
- fontFamily: theme.fontFamily,
254
- }}
255
- >
256
- {options.map((option, index) => (
257
- <option key={option.label + index} value={option.value}>
258
- {option.label}
259
- </option>
260
- ))}
261
- </select>
262
- );
263
- }
264
- }
265
- ButtonComponent={
266
- ButtonComponent
267
- ? ButtonComponent
268
- : ({ onClick, label }: ButtonComponentProps) => {
269
- return (
270
- <div
271
- style={{
272
- height: 32,
273
- background: theme.primaryButtonColor,
274
- borderWidth: theme.borderWidth,
275
- borderColor: theme.borderColor,
276
- color: theme.backgroundColor,
277
- display: 'flex',
278
- borderRadius: 6,
279
- alignItems: 'center',
280
- justifyContent: 'center',
281
- outline: 'none',
282
- cursor: 'pointer',
283
- fontFamily: theme.fontFamily,
284
- fontWeight: theme.buttonFontWeight || 600,
285
- }}
286
- onClick={onClick}
287
- >
288
- {label}
289
- </div>
290
- );
291
- }
292
- }
293
- ModalComponent={
294
- ModalComponent
295
- ? ModalComponent
296
- : ({ children, isOpen, onClose, title }: ModalComponentProps) => {
297
- return (
298
- <QuillModal
299
- isOpen={isOpen}
300
- theme={theme}
301
- onClose={onClose}
302
- title={title}
303
- >
304
- {children}
305
- </QuillModal>
306
- );
307
- }
308
- }
309
- ModalTriggerComponent={
310
- ModalTriggerComponent
311
- ? ModalTriggerComponent
312
- : ({ onClick, label }: ModalTriggerComponentProps) => {
313
- return (
314
- <div
315
- style={{
316
- height: 32,
317
- background: theme.backgroundColor,
318
- borderWidth: theme.borderWidth,
319
- borderColor: theme.borderColor,
320
- borderStyle: 'solid',
321
- outline: 'none',
322
- cursor: 'pointer',
323
- display: 'flex',
324
- borderRadius: 6,
325
- alignItems: 'center',
326
- justifyContent: 'center',
327
- boxShadow: '0 1px 2px 0 rgba(0,0,0,.05)',
328
- paddingRight: 12,
329
- paddingLeft: 12,
330
- fontSize: 14,
331
- color: theme.primaryTextColor,
332
- fontWeight: theme.buttonFontWeight || 600,
333
- fontFamily: theme.fontFamily,
334
- }}
335
- onClick={onClick}
336
- >
337
- {label}
338
- </div>
339
- );
340
- }
341
- }
342
- TextInputComponent={
343
- TextInputComponent
344
- ? TextInputComponent
345
- : ({ onChange, value, id }: TextInputComponentProps) => {
346
- return (
347
- <input
348
- style={{
349
- outline: 'none',
350
- textAlign: 'left',
351
- whiteSpace: 'nowrap',
352
- overflow: 'hidden',
353
- textOverflow: 'ellipsis',
354
- borderRadius: 6,
355
- paddingLeft: 12,
356
- paddingRight: 12,
357
- height: 38,
358
- borderWidth: theme.borderWidth,
359
- borderColor: theme.borderColor,
360
- borderStyle: 'solid',
361
- background: theme.backgroundColor,
362
- color: theme.primaryTextColor,
363
- boxShadow: '0 1px 2px 0 rgba(0,0,0,.05)',
364
- fontFamily: theme.fontFamily,
365
- }}
366
- id={id}
367
- onChange={onChange}
368
- value={value}
369
- />
370
- );
371
- }
372
- }
373
- tagStyle={tagStyle}
374
- />
375
- );
376
- }
377
-
378
- export function getPostgresBasicType(column) {
379
- let format;
380
-
381
- // first check if column.dataTypeID exists
382
- if (column.dataTypeID) {
383
- switch (column.dataTypeID) {
384
- case 20: // int8
385
- case 21: // int2
386
- case 23: // int4
387
- case 700: // float4
388
- case 701: // float8
389
- case 1700: // numeric
390
- format = 'number';
391
- break;
392
- case 1082: // date
393
- case 1083: // time
394
- case 1184: // timestamptz
395
- case 1114: // timestamp
396
- format = 'date';
397
- break;
398
- case 1043: // varchar
399
- default:
400
- format = 'string';
401
- }
402
- } else if (column.fieldType) {
403
- // if column.dataTypeID doesn't exist, check column.fieldType
404
- switch (column.fieldType) {
405
- case 'int8':
406
- case 'int2':
407
- case 'int4':
408
- case 'float4':
409
- case 'float8':
410
- case 'numeric':
411
- format = 'number';
412
- break;
413
- case 'date':
414
- case 'time':
415
- case 'timestamptz':
416
- case 'timestamp':
417
- format = 'date';
418
- break;
419
- case 'varchar':
420
- default:
421
- format = 'string';
422
- }
423
- }
424
-
425
- return format;
426
- }
427
-
428
- function ReportingTool({
429
- schema,
430
- data,
431
- runQuery,
432
- SelectComponent,
433
- ButtonComponent,
434
- onChangeQuery,
435
- theme,
436
- ModalComponent,
437
- ModalTriggerComponent,
438
- TextInputComponent,
439
- tagStyle,
440
- }) {
441
- const [selectedTable, setSelectedTable] = useState(schema[0]);
442
- const [selectedColumn, setSelectedColumn] = useState(schema[0].columns[0]);
443
- const [filters, setFilters] = useState([]);
444
- const [AST, setAST] = useState({
445
- with: null,
446
- type: 'select',
447
- options: null,
448
- distinct: { type: null },
449
- columns: '*',
450
- into: { position: null },
451
- from: [{ db: null, table: schema[0].displayName, as: null }],
452
- where: null,
453
- groupby: null,
454
- having: null,
455
- orderby: null,
456
- limit: { seperator: '', value: [] },
457
- window: null,
458
- });
459
- const [numberStart, setNumberStart] = useState(0);
460
- const [numberEnd, setNumberEnd] = useState(0);
461
- const [dateStart, setDateStart] = useState('');
462
- const [dateEnd, setDateEnd] = useState('');
463
- const [computedColumns, setComputedColumns] = useState({});
464
- const [stringFilterValues, setStringFilterValues] = useState([]);
465
- const [columnType, setColumnType] = useState(
466
- getPostgresBasicType(schema[0].columns[0])
467
- );
468
- const [sqlQuery, setSqlQuery] = useState('');
469
- // month | week | day | quarter
470
- const [dateBucket, setDateBucket] = useState('month');
471
- const [indexBeingEdited, setIndexBeingEdited] = useState(-1);
472
- const [groupBys, setGroupBys] = useState([]);
473
- const [aggregations, setAggregations] = useState([]);
474
- const [selectedGroupByColumn, setSelectedGroupByColumn] = useState(
475
- schema[0].columns[0]
476
- );
477
- const [groupByColumnType, setGroupByColumnType] = useState(
478
- getPostgresBasicType(schema[0].columns[0])
479
- );
480
- const [selectedSortByColumn, setSelectedSortByColumn] = useState(
481
- schema[0].columns[0].name
482
- );
483
- const [sortBys, setSortBys] = useState([]);
484
- const [selectedSortByDirection, setSelectedSortByDirection] =
485
- useState('ascending');
486
-
487
- const [groupByIndexBeingEdited, setGroupByIndexBeingEdited] = useState(-1);
488
-
489
- const [sortByIndexBeingEdited, setSortByIndexBeingEdited] = useState(-1);
490
- const [sortableColumns, setSortableColumns] = useState(
491
- schema[0].columns.map(col => col.name)
492
- );
493
-
494
- const addGroupBy = () => {
495
- if (selectedGroupByColumn && groupByColumnType) {
496
- if (groupByColumnType === 'string') {
497
- setGroupBys(groupBys => {
498
- return [
499
- ...groupBys,
500
- {
501
- column: selectedGroupByColumn.name,
502
- columnType: groupByColumnType,
503
- tag: selectedGroupByColumn.name,
504
- },
505
- ];
506
- });
507
- return;
508
- } else if (groupByColumnType === 'number') {
509
- // is this possible lmao
510
- return;
511
- } else if (groupByColumnType === 'date') {
512
- setGroupBys(groupBys => {
513
- return [
514
- ...groupBys,
515
- {
516
- column: selectedGroupByColumn.name,
517
- columnType: groupByColumnType,
518
- bucket: dateBucket,
519
- tag: dateBucket,
520
- },
521
- ];
522
- });
523
- return;
524
- }
525
- }
526
- };
527
-
528
- const addSortBy = () => {
529
- setSortBys(sortBys => {
530
- return [
531
- ...sortBys,
532
- {
533
- column: selectedSortByColumn,
534
- direction: selectedSortByDirection,
535
- },
536
- ];
537
- });
538
- };
539
-
540
- const removeSortBy = index => {
541
- setSortBys(oldSortBys => {
542
- const newSortBys = [...oldSortBys];
543
- newSortBys.splice(index, 1);
544
- return newSortBys;
545
- });
546
- setSortByIndexBeingEdited(-1);
547
- };
548
-
549
- const removeGroupBy = index => {
550
- const columnBeingDeleted = groupBys[index].tag;
551
- setSortBys(oldSortBys => {
552
- const newSortBys = [
553
- ...oldSortBys.filter(sortBy => sortBy.column !== columnBeingDeleted),
554
- ];
555
- return newSortBys;
556
- });
557
- setGroupBys(oldGroupBys => {
558
- const newGroupBys = [...oldGroupBys];
559
- newGroupBys.splice(index, 1);
560
- return newGroupBys;
561
- });
562
- setGroupByIndexBeingEdited(-1);
563
- };
564
-
565
- const updateSortBy = index => {
566
- if (selectedSortByColumn && selectedSortByDirection) {
567
- setSortBys(sortBys => {
568
- const newSortBys = [...sortBys];
569
- newSortBys[index] = {
570
- column: selectedSortByColumn,
571
- direction: selectedSortByDirection,
572
- };
573
- return newSortBys;
574
- });
575
- setSortByIndexBeingEdited(-1);
576
- return;
577
- }
578
- };
579
-
580
- useEffect(() => {
581
- setColumnType(getPostgresBasicType(selectedColumn));
582
- // console.log("WTF: ", selectedColumn);
583
- }, [selectedColumn]);
584
-
585
- useEffect(() => {
586
- setGroupByColumnType(getPostgresBasicType(selectedGroupByColumn));
587
- }, [selectedGroupByColumn]);
588
-
589
- const selectFilter = index => {
590
- const filter = filters[index];
591
- const matchingColumn = selectedTable.columns.find(
592
- column => column.name === filter.column
593
- );
594
- setSelectedColumn(matchingColumn);
595
- if (filter.columnType === 'string') {
596
- setStringFilterValues(filter.stringFilterValues);
597
- setIndexBeingEdited(index);
598
- } else if (filter.columnType === 'number') {
599
- setNumberStart(filter.numberStart);
600
- setNumberEnd(filter.numberEnd);
601
- setIndexBeingEdited(index);
602
- } else if (filter.columnType === 'date') {
603
- setDateStart(filter.dateStart);
604
- setDateEnd(filter.dateEnd);
605
- setIndexBeingEdited(index);
606
- }
607
- };
608
-
609
- const selectSortBy = index => {
610
- const sortBy = sortBys[index];
611
- setSelectedSortByColumn(sortBy.column);
612
- setSelectedSortByDirection(sortBy.direction);
613
- setSortByIndexBeingEdited(index);
614
- };
615
-
616
- const selectGroupBy = index => {
617
- const groupBy = groupBys[index];
618
- const matchingColumn = selectedTable.columns.find(
619
- column => column.name === groupBy.column
620
- );
621
- setSelectedGroupByColumn(matchingColumn);
622
- if (groupBy.bucket) {
623
- setDateBucket(groupBy.bucket);
624
- }
625
- setGroupByIndexBeingEdited(index);
626
- };
627
-
628
- const updateGroupBy = index => {
629
- if (selectedGroupByColumn && groupByColumnType) {
630
- // if column changed, then auto delete the sort using that column
631
- const columnBeingDeleted =
632
- selectedGroupByColumn.name !== groupBys[index].column
633
- ? groupBys[index].tag
634
- : 'nocolumnbeingdeleted';
635
- setSortBys(oldSortBys => {
636
- const newSortBys = [
637
- ...oldSortBys.filter(sortBy => sortBy.column !== columnBeingDeleted),
638
- ];
639
- return newSortBys;
640
- });
641
- if (groupByColumnType === 'date') {
642
- setGroupBys(groupBys => {
643
- const newGroupBys = [...groupBys];
644
- newGroupBys[index] = {
645
- column: selectedGroupByColumn.name,
646
- columnType: groupByColumnType,
647
- bucket: dateBucket,
648
- tag: dateBucket,
649
- };
650
- return newGroupBys;
651
- });
652
- } else {
653
- setGroupBys(groupBys => {
654
- const newGroupBys = [...groupBys];
655
- newGroupBys[index] = {
656
- column: selectedGroupByColumn.name,
657
- columnType: groupByColumnType,
658
- tag: selectedGroupByColumn.name,
659
- };
660
- return newGroupBys;
661
- });
662
- }
663
- setGroupByIndexBeingEdited(-1);
664
- return;
665
- }
666
- };
667
-
668
- const updateFilter = index => {
669
- if (selectedColumn && columnType) {
670
- if (columnType === 'string') {
671
- setFilters(filters => {
672
- const newFilters = [...filters];
673
- newFilters[index] = {
674
- column: selectedColumn.name,
675
- columnType,
676
- stringFilterValues,
677
- tag: `${selectedColumn.name} (${stringFilterValues.join(', ')})`,
678
- };
679
- return newFilters;
680
- });
681
- } else if (columnType === 'number') {
682
- setFilters(filters => {
683
- const newFilters = [...filters];
684
- newFilters[index] = {
685
- column: selectedColumn.name,
686
- columnType,
687
- numberStart,
688
- numberEnd,
689
- tag: `${numberStart} < ${selectedColumn.name} < ${numberEnd}`,
690
- };
691
- return newFilters;
692
- });
693
- } else if (columnType === 'date') {
694
- setFilters(filters => {
695
- const newFilters = [...filters];
696
- newFilters[index] = {
697
- column: selectedColumn.name,
698
- columnType,
699
- dateStart,
700
- dateEnd,
701
- tag: `${selectedColumn.name} (${format(
702
- new Date(dateStart),
703
- 'MMM dd'
704
- )} - ${format(new Date(dateEnd), 'MMM dd')})`,
705
- };
706
- return newFilters;
707
- });
708
- }
709
-
710
- setStringFilterValues([]);
711
- setNumberStart(0);
712
- setNumberEnd(0);
713
- setDateStart('');
714
- setDateEnd('');
715
- setIndexBeingEdited(-1);
716
- return;
717
- }
718
- };
719
-
720
- // ADD FILTER TO "FILTERS" ARRAY
721
- const addFilter = () => {
722
- if (selectedColumn && columnType) {
723
- // const type = getPostgresBasicType(selectedColumn);
724
- let newCondition;
725
-
726
- if (columnType === 'string') {
727
- setFilters(filters => {
728
- return [
729
- ...filters,
730
- {
731
- column: selectedColumn.name,
732
- columnType,
733
- stringFilterValues,
734
- tag: `${selectedColumn.name} (${stringFilterValues.join(', ')})`,
735
- },
736
- ];
737
- });
738
- setStringFilterValues([]);
739
- setNumberStart(0);
740
- setNumberEnd(0);
741
- setDateStart('');
742
- setDateEnd('');
743
- return;
744
- } else if (columnType === 'number') {
745
- setFilters(filters => {
746
- return [
747
- ...filters,
748
- {
749
- column: selectedColumn.name,
750
- columnType,
751
- numberStart,
752
- numberEnd,
753
- tag: `${numberStart} < ${selectedColumn.name} < ${numberEnd}`,
754
- },
755
- ];
756
- });
757
- setStringFilterValues([]);
758
- setNumberStart(0);
759
- setNumberEnd(0);
760
- setDateStart('');
761
- setDateEnd('');
762
- return;
763
- } else if (columnType === 'date') {
764
- setFilters(filters => {
765
- return [
766
- ...filters,
767
- {
768
- column: selectedColumn.name,
769
- columnType,
770
- dateStart,
771
- dateEnd,
772
- tag: `${selectedColumn.name} (${format(
773
- new Date(dateStart),
774
- 'MMM dd'
775
- )} - ${format(new Date(dateEnd), 'MMM dd')})`,
776
- },
777
- ];
778
- });
779
- setStringFilterValues([]);
780
- setNumberStart(0);
781
- setNumberEnd(0);
782
- setDateStart('');
783
- setDateEnd('');
784
- }
785
- }
786
- };
787
-
788
- const setAggregationColumn = (column, index) => {
789
- // ex
790
- setAggregations(aggregations => {
791
- const newAggregations = [...aggregations];
792
- newAggregations[index] = {
793
- ...newAggregations[index],
794
- column: column,
795
- };
796
- return newAggregations;
797
- });
798
- };
799
-
800
- const setAggregationType = (aggregationType, index) => {
801
- // ex
802
- setAggregations(aggregations => {
803
- const newAggregations = [...aggregations];
804
- newAggregations[index] = {
805
- ...newAggregations[index],
806
- aggregationType: aggregationType,
807
- };
808
- return newAggregations;
809
- });
810
- };
811
-
812
- const addAggregation = () => {
813
- // setAggregations([
814
- // {
815
- // column: selectedTable.columns.filter(
816
- // (col) => getPostgresBasicType(col) === "number"
817
- // )[0],
818
- // aggregationType: "sum",
819
- // },
820
- // ]);
821
- setAggregations(aggregations => {
822
- const newAggregations = [
823
- ...aggregations,
824
- {
825
- column: selectedTable.columns.filter(
826
- col =>
827
- getPostgresBasicType(col) === 'number' &&
828
- !aggregations.map(elem => elem.name).includes(col.name)
829
- )[0],
830
- aggregationType: 'sum',
831
- },
832
- ];
833
- return newAggregations;
834
- });
835
- };
836
-
837
- useEffect(() => {
838
- // if selected table changes, clear everything
839
- if (selectedTable.displayName !== AST.from.table) {
840
- setSelectedColumn(selectedTable.columns[0]);
841
- setSortableColumns(selectedTable.columns.map(col => col.name));
842
- setGroupBys([]);
843
- setSortBys([]);
844
- setFilters([]);
845
- // setAST((AST) => {
846
- // return {
847
- // with: null,
848
- // type: "select",
849
- // options: null,
850
- // distinct: { type: null },
851
- // columns: "*",
852
- // into: { position: null },
853
- // where: null,
854
- // groupby: null,
855
- // having: null,
856
- // orderby: null,
857
- // limit: { seperator: "", value: [] },
858
- // window: null,
859
- // from: [{ db: null, table: selectedTable.displayName, as: null }],
860
- // };
861
- // });
862
- return;
863
- }
864
- }, [selectedTable]);
865
-
866
- // USE EFFECT HOOK THAT TRANSFORMS "FILTERS ARRAY INTO AST"
867
- useEffect(() => {
868
- if (filters.length || groupBys.length || aggregations.length) {
869
- const newAST = {
870
- with: null,
871
- type: 'select',
872
- options: null,
873
- distinct: null,
874
- columns: null,
875
- into: { position: null },
876
- from: [{ db: null, table: selectedTable.displayName, as: null }],
877
- where: null,
878
- groupby: null,
879
- having: null,
880
- orderby: null,
881
- limit: null,
882
- window: null,
883
- };
884
- // FILTERS
885
- for (let i = 0; i < filters.length; i++) {
886
- const filter = filters[i];
887
- const {
888
- column,
889
- columnType,
890
- stringFilterValues,
891
- numberStart,
892
- numberEnd,
893
- dateStart,
894
- dateEnd,
895
- } = filter;
896
- let newCondition;
897
- if (columnType === 'string') {
898
- newCondition = {
899
- type: 'binary_expr',
900
- operator: 'IN',
901
- left: {
902
- type: 'column_ref',
903
- table: null,
904
- column: column,
905
- },
906
- right: {
907
- type: 'expr_list',
908
- value: stringFilterValues.map(value => ({
909
- type: 'single_quote_string',
910
- value,
911
- })),
912
- },
913
- };
914
- } else if (columnType === 'number') {
915
- newCondition = {
916
- type: 'binary_expr',
917
- operator: 'BETWEEN',
918
- left: {
919
- type: 'column_ref',
920
- table: null,
921
- column: column,
922
- },
923
- right: {
924
- type: 'expr_list',
925
- value: [
926
- { type: 'number', value: numberStart },
927
- { type: 'number', value: numberEnd },
928
- ],
929
- },
930
- };
931
- } else if (columnType === 'date') {
932
- newCondition = {
933
- type: 'binary_expr',
934
- operator: 'BETWEEN',
935
- left: {
936
- type: 'column_ref',
937
- table: null,
938
- column: column,
939
- },
940
- right: {
941
- type: 'expr_list',
942
- value: [
943
- {
944
- type: 'single_quote_string',
945
- value: format(new Date(dateStart), 'MM/dd/yyyy'),
946
- },
947
- {
948
- type: 'single_quote_string',
949
- value: format(new Date(dateEnd), 'MM/dd/yyyy'),
950
- },
951
- ],
952
- },
953
- };
954
- }
955
-
956
- if (!newAST.where) {
957
- newAST.where = newCondition;
958
- } else {
959
- newAST.where = {
960
- type: 'binary_expr',
961
- operator: 'AND',
962
- left: newAST.where,
963
- right: newCondition,
964
- };
965
- }
966
- }
967
-
968
- // GROUP BYS and AGGREGATIONS
969
- // if (groupBys.length > 0) {
970
- // newAST.groupby = groupBys.map((groupBy) => ({
971
- // type: "column_ref",
972
- // table: null,
973
- // column: groupBy.column,
974
- // }));
975
-
976
- // if (aggregations.length > 0) {
977
- // newAST.columns = [
978
- // ...groupBys.map((groupBy) => ({
979
- // expr: {
980
- // type: "column_ref",
981
- // table: null,
982
- // column: groupBy.column,
983
- // },
984
- // as: null,
985
- // })),
986
- // ...aggregations.map((aggregation) => ({
987
- // expr: {
988
- // type: "aggr_func",
989
- // name: aggregation.aggregationType.toUpperCase(),
990
- // args: {
991
- // expr: {
992
- // type: "column_ref",
993
- // table: null,
994
- // column: aggregation.column.name,
995
- // },
996
- // },
997
- // over: null,
998
- // },
999
- // as: null,
1000
- // })),
1001
- // ];
1002
- // } else {
1003
- // newAST.columns = groupBys.map((groupBy) => ({
1004
- // expr: {
1005
- // type: "column_ref",
1006
- // table: null,
1007
- // column: groupBy.column,
1008
- // },
1009
- // as: null,
1010
- // }));
1011
- // }
1012
- // } else {
1013
- // newAST.columns = "*";
1014
- // newAST.groupby = null;
1015
- // }
1016
-
1017
- // GROUP BYS
1018
- if (groupBys.length > 0) {
1019
- newAST.columns = [];
1020
- newAST.groupby = [];
1021
-
1022
- for (let i = 0; i < groupBys.length; i++) {
1023
- const groupBy = groupBys[i];
1024
- if (groupBy.columnType === 'date') {
1025
- newAST.columns.push({
1026
- expr: {
1027
- type: 'function',
1028
- name: 'date_trunc',
1029
- args: {
1030
- type: 'expr_list',
1031
- value: [
1032
- {
1033
- type: 'single_quote_string',
1034
- value: groupBy.bucket,
1035
- },
1036
- {
1037
- type: 'column_ref',
1038
- table: null,
1039
- column: groupBy.column,
1040
- },
1041
- ],
1042
- },
1043
- over: null,
1044
- },
1045
- as: groupBy.bucket,
1046
- });
1047
- newAST.groupby.push({
1048
- type: 'column_ref',
1049
- table: null,
1050
- column: groupBy.bucket,
1051
- });
1052
- } else {
1053
- newAST.columns.push({
1054
- expr: {
1055
- type: 'column_ref',
1056
- table: null,
1057
- column: groupBy.column,
1058
- },
1059
- as: null,
1060
- });
1061
- newAST.groupby.push({
1062
- type: 'column_ref',
1063
- table: null,
1064
- column: groupBy.column,
1065
- });
1066
- }
1067
- }
1068
-
1069
- // AGGREGATIONS
1070
- if (aggregations.length > 0) {
1071
- for (let i = 0; i < aggregations.length; i++) {
1072
- const aggregation = aggregations[i];
1073
- newAST.columns.push({
1074
- expr: {
1075
- type: 'aggr_func',
1076
- name: aggregation.aggregationType.toUpperCase(),
1077
- args: {
1078
- expr: {
1079
- type: 'column_ref',
1080
- table: null,
1081
- column: aggregation.column.name,
1082
- },
1083
- },
1084
- over: null,
1085
- },
1086
- as: null,
1087
- });
1088
- }
1089
- }
1090
- } else {
1091
- newAST.columns = '*';
1092
- newAST.groupby = null;
1093
- }
1094
-
1095
- if (sortBys.length > 0) {
1096
- newAST.orderby = [];
1097
- for (let i = 0; i < sortBys.length; i++) {
1098
- const sortBy = sortBys[i];
1099
- newAST.orderby.push({
1100
- expr: { type: 'column_ref', table: null, column: sortBy.column },
1101
- type: sortBy.direction === 'descending' ? 'DESC' : 'ASC',
1102
- });
1103
- }
1104
- // "orderby":[{"expr":{"type":"column_ref","table":null,"column":"amount"},"type":"DESC"},{"expr":{"type":"column_ref","table":null,"column":"month"},"type":"ASC"}]
1105
- }
1106
-
1107
- setAST(newAST);
1108
- }
1109
- }, [filters, groupBys, aggregations, sortBys]);
1110
-
1111
- const removeFilter = index => {
1112
- setFilters(oldFilters => {
1113
- const newFilters = [...oldFilters];
1114
- newFilters.splice(index, 1);
1115
- return newFilters;
1116
- });
1117
- setIndexBeingEdited(-1);
1118
- };
1119
-
1120
- const computeStats = useCallback(
1121
- column => {
1122
- if (!computedColumns[column.name] && data) {
1123
- const basicType = getPostgresBasicType(column);
1124
- let result;
1125
-
1126
- if (basicType === 'number') {
1127
- let min = Infinity,
1128
- max = -Infinity;
1129
- data.forEach(row => {
1130
- const value = row[column.name];
1131
- min = Math.min(min, value);
1132
- max = Math.max(max, value);
1133
- });
1134
- result = { min, max };
1135
- } else if (basicType === 'string') {
1136
- const freqMap = {};
1137
- data.forEach(row => {
1138
- const value = row[column.name];
1139
- freqMap[value] = (freqMap[value] || 0) + 1;
1140
- });
1141
- result = Object.entries(freqMap)
1142
- .sort((a, b) => b[1] - a[1])
1143
- .slice(0, 6)
1144
- .map(([key]) => key);
1145
- } else {
1146
- // Handle other column types if necessary
1147
- }
1148
-
1149
- setComputedColumns({
1150
- ...computedColumns,
1151
- [column.name]: result,
1152
- });
1153
- }
1154
- },
1155
- [data, computedColumns]
1156
- );
1157
-
1158
- // Call this function whenever the selected column changes
1159
- useEffect(() => {
1160
- computeStats(selectedColumn);
1161
- }, [selectedColumn]);
1162
-
1163
- // Use the results directly in your component
1164
- const columnStats = computedColumns[selectedColumn.name];
1165
-
1166
- // useEffect(() => {
1167
- // if (AST && AST.from[0].table) {
1168
- // const parser = new Parser();
1169
- // const sqlQuery = parser.sqlify(AST, { database: "PostgresQL" });
1170
- // if (sqlQuery) {
1171
- // runQuery(sqlQuery);
1172
- // return;
1173
- // }
1174
- // }
1175
- // }, [AST]);
1176
-
1177
- useEffect(() => {
1178
- if (!aggregations.length) {
1179
- setAggregations([
1180
- {
1181
- column: selectedTable.columns.filter(
1182
- col => getPostgresBasicType(col) === 'number'
1183
- )[0],
1184
- aggregationType: 'sum',
1185
- },
1186
- ]);
1187
- }
1188
- }, [selectedGroupByColumn]);
1189
-
1190
- useEffect(() => {
1191
- const getSqlQueryFromAST = async () => {
1192
- try {
1193
- if (AST && AST.from[0].table) {
1194
- const response = await axios.post(
1195
- 'https://quill-344421.uc.r.appspot.com/sqlify',
1196
- { ast: AST }
1197
- );
1198
- const sqlQuery = response.data.query; // assuming the response contains the SQL query
1199
- // alert(sqlQuery);
1200
- if (sqlQuery) {
1201
- onChangeQuery(sqlQuery);
1202
- runQuery(sqlQuery);
1203
- setSqlQuery(sqlQuery);
1204
- if (AST.columns === '*') {
1205
- setSortableColumns(selectedTable.columns.map(col => col.name));
1206
- } else if (AST.columns.length) {
1207
- setSortableColumns(
1208
- AST.columns.map(elem => elem.as || elem.expr.name)
1209
- );
1210
- }
1211
- }
1212
- }
1213
- } catch (err) {
1214
- console.error(err);
1215
- }
1216
- };
1217
-
1218
- getSqlQueryFromAST();
1219
- }, [AST]);
1220
-
1221
- useEffect(() => {
1222
- if (sortableColumns.length) {
1223
- setSelectedSortByColumn(sortableColumns[0]);
1224
- }
1225
- }, [sortableColumns]);
1226
-
1227
- if (!schema || !schema.length) {
1228
- return null;
1229
- }
1230
-
1231
- return (
1232
- <div style={{ marginLeft: '25px' }}>
1233
- <div style={{ maxWidth: 245 }}>
1234
- <div
1235
- style={{
1236
- fontSize: 14,
1237
- marginBottom: '6px',
1238
- fontWeight: '600',
1239
- color: theme.secondaryTextColor,
1240
- fontFamily: theme?.fontFamily,
1241
- }}
1242
- >
1243
- Table
1244
- </div>
1245
- <SelectComponent
1246
- label="Table"
1247
- value={selectedTable.displayName}
1248
- onChange={e => {
1249
- const table = schema.find(t => t.displayName === e);
1250
- setSelectedTable(table);
1251
- }}
1252
- options={
1253
- schema?.length
1254
- ? schema.map(elem => {
1255
- return { label: elem.displayName, value: elem.displayName };
1256
- })
1257
- : []
1258
- }
1259
- />
1260
- </div>
1261
- <div
1262
- style={{ display: 'flex', flexDirection: 'column', marginTop: '12px' }}
1263
- >
1264
- {/* <AddFilterModal
1265
- filters={filters}
1266
- selectedColumn={selectedColumn}
1267
- numberStart={numberStart}
1268
- numberEnd={numberEnd}
1269
- dateStart={dateStart}
1270
- setDateStart={setDateStart}
1271
- columnStats={columnStats}
1272
- stringFilterValues={stringFilterValues}
1273
- setStringFilterValues={setStringFilterValues}
1274
- addFilter={addFilter}
1275
- setSelectedColumn={setSelectedColumn}
1276
- setNumberStart={setNumberStart}
1277
- setNumberEnd={setNumberEnd}
1278
- selectedTable={selectedTable}
1279
- columnType={columnType}
1280
- dateEnd={dateEnd}
1281
- setDateEnd={setDateEnd}
1282
- removeFilter={removeFilter}
1283
- selectFilter={selectFilter}
1284
- ref={ref}
1285
- indexBeingEdited={indexBeingEdited}
1286
- updateFilter={updateFilter}
1287
- SelectComponent={SelectComponent}
1288
- /> */}
1289
-
1290
- <AddFilterModal2
1291
- filters={filters}
1292
- selectedColumn={selectedColumn}
1293
- numberStart={numberStart}
1294
- numberEnd={numberEnd}
1295
- dateStart={dateStart}
1296
- setDateStart={setDateStart}
1297
- columnStats={columnStats}
1298
- stringFilterValues={stringFilterValues}
1299
- setStringFilterValues={setStringFilterValues}
1300
- addFilter={addFilter}
1301
- setSelectedColumn={setSelectedColumn}
1302
- setNumberStart={setNumberStart}
1303
- setNumberEnd={setNumberEnd}
1304
- selectedTable={selectedTable}
1305
- columnType={columnType}
1306
- dateEnd={dateEnd}
1307
- setDateEnd={setDateEnd}
1308
- removeFilter={removeFilter}
1309
- selectFilter={selectFilter}
1310
- indexBeingEdited={indexBeingEdited}
1311
- updateFilter={updateFilter}
1312
- SelectComponent={SelectComponent}
1313
- ButtonComponent={ButtonComponent}
1314
- ModalComponent={ModalComponent}
1315
- ModalTriggerComponent={ModalTriggerComponent}
1316
- TextInputComponent={TextInputComponent}
1317
- tagStyle={tagStyle}
1318
- theme={theme}
1319
- />
1320
-
1321
- {/* <div style={{ height: 12 }} /> */}
1322
-
1323
- <GroupByModal2
1324
- selectedTable={selectedTable}
1325
- groupBys={groupBys}
1326
- selectedGroupByColumn={selectedGroupByColumn}
1327
- setSelectedGroupByColumn={setSelectedGroupByColumn}
1328
- addGroupBy={addGroupBy}
1329
- groupByColumnType={groupByColumnType}
1330
- removeGroupBy={removeGroupBy}
1331
- selectGroupBy={selectGroupBy}
1332
- groupByIndexBeingEdited={groupByIndexBeingEdited}
1333
- updateGroupBy={updateGroupBy}
1334
- aggregations={aggregations}
1335
- setAggregationColumn={setAggregationColumn}
1336
- setAggregationType={setAggregationType}
1337
- SelectComponent={SelectComponent}
1338
- ButtonComponent={ButtonComponent}
1339
- ModalComponent={ModalComponent}
1340
- ModalTriggerComponent={ModalTriggerComponent}
1341
- TextInputComponent={TextInputComponent}
1342
- tagStyle={tagStyle}
1343
- addAggregation={addAggregation}
1344
- dateBucket={dateBucket}
1345
- setDateBucket={setDateBucket}
1346
- theme={theme}
1347
- />
1348
-
1349
- <SortByModal
1350
- selectedTable={selectedTable}
1351
- sortableColumns={sortableColumns}
1352
- sortBys={sortBys}
1353
- selectedSortByColumn={selectedSortByColumn}
1354
- setSelectedSortByColumn={setSelectedSortByColumn}
1355
- selectedSortByDirection={selectedSortByDirection}
1356
- setSelectedSortByDirection={setSelectedSortByDirection}
1357
- addSortBy={addSortBy}
1358
- removeSortBy={removeSortBy}
1359
- selectSortBy={selectSortBy}
1360
- sortByIndexBeingEdited={sortByIndexBeingEdited}
1361
- updateSortBy={updateSortBy}
1362
- SelectComponent={SelectComponent}
1363
- ButtonComponent={ButtonComponent}
1364
- ModalComponent={ModalComponent}
1365
- ModalTriggerComponent={ModalTriggerComponent}
1366
- tagStyle={tagStyle}
1367
- theme={theme}
1368
- />
1369
- </div>
1370
- </div>
1371
- );
1372
- }
1373
-
1374
- function FilterTag({
1375
- id,
1376
- label,
1377
- removeFilter,
1378
- index,
1379
- setIsOpen,
1380
- selectFilter,
1381
- theme,
1382
- tagStyle,
1383
- }) {
1384
- const handleRemoveFilter = () => {
1385
- removeFilter(index);
1386
- };
1387
- const handleSelectFilter = () => {
1388
- selectFilter(index);
1389
- setIsOpen(true);
1390
- };
1391
- return (
1392
- <div
1393
- id={id}
1394
- onClick={handleSelectFilter}
1395
- style={
1396
- tagStyle || {
1397
- marginLeft: '12px',
1398
- cursor: 'pointer',
1399
- borderRadius: 8,
1400
- backgroundColor: '#EFF0FC',
1401
- paddingLeft: '12px',
1402
- paddingRight: '8px',
1403
- height: 30,
1404
- display: 'flex',
1405
- alignItems: 'center',
1406
- fontSize: 13,
1407
- fontWeight: 'medium',
1408
- color: theme?.primaryTextColor,
1409
- fontFamily: theme?.fontFamily,
1410
- outline: 'none',
1411
- }
1412
- }
1413
- >
1414
- <div
1415
- id={id}
1416
- style={{
1417
- textOverflow: 'ellipsis',
1418
- whiteSpace: 'nowrap',
1419
- overflow: 'hidden',
1420
- maxWidth: '80px',
1421
- }}
1422
- >
1423
- {label}
1424
- </div>
1425
- <div
1426
- // onClick={handleRemoveFilter}
1427
- onClick={e => {
1428
- e.stopPropagation(); // Prevents the event from bubbling up to the parent
1429
- handleRemoveFilter();
1430
- }}
1431
- style={{
1432
- display: 'flex',
1433
- flexDirection: 'row',
1434
- alignItems: 'center',
1435
- cursor: 'pointer',
1436
- paddingLeft: '6px',
1437
- }}
1438
- >
1439
- <XMarkIcon
1440
- style={{
1441
- height: '20px',
1442
- width: '20px',
1443
- color: tagStyle?.color || theme?.primaryTextColor,
1444
- }}
1445
- aria-hidden="true"
1446
- />
1447
- </div>
1448
- </div>
1449
- );
1450
- }
1451
-
1452
- const SortByModal = ({
1453
- selectedSortByColumn,
1454
- selectedSortByDirection,
1455
- setSelectedSortByColumn,
1456
- setSelectedSortByDirection,
1457
- selectedTable,
1458
- sortableColumns,
1459
- removeSortBy,
1460
- selectSortBy,
1461
- updateSortBy,
1462
- addSortBy,
1463
- sortBys,
1464
- SelectComponent,
1465
- ButtonComponent,
1466
- ModalComponent,
1467
- tagStyle,
1468
- ModalTriggerComponent,
1469
- sortByIndexBeingEdited,
1470
- theme,
1471
- }) => {
1472
- const [isOpen, setIsOpen] = useState(false);
1473
- return (
1474
- <div style={{ display: 'flex', flexDirection: 'column', marginTop: 12 }}>
1475
- <div
1476
- style={{
1477
- position: 'relative',
1478
- display: 'inline-block',
1479
- textAlign: 'left',
1480
- }}
1481
- >
1482
- <div
1483
- style={{
1484
- display: 'flex',
1485
- flexDirection: 'row',
1486
- alignItems: 'center',
1487
- }}
1488
- >
1489
- <ModalTriggerComponent
1490
- onClick={() => setIsOpen(isOpen => !isOpen)}
1491
- label="Sort"
1492
- />
1493
- <div
1494
- style={{
1495
- overflowX: 'scroll',
1496
- display: 'flex',
1497
- flexDirection: 'row',
1498
- alignItems: 'center',
1499
- }}
1500
- >
1501
- {sortBys.map((elem, index) => (
1502
- <FilterTag
1503
- id="sort-tag"
1504
- label={elem.column}
1505
- removeFilter={removeSortBy}
1506
- selectFilter={selectSortBy}
1507
- setIsOpen={setIsOpen}
1508
- index={index}
1509
- theme={theme}
1510
- tagStyle={tagStyle}
1511
- key={'sort' + index}
1512
- />
1513
- ))}
1514
- </div>
1515
- </div>
1516
-
1517
- <ModalComponent
1518
- isOpen={isOpen}
1519
- onClose={() => setIsOpen(false)}
1520
- title="Add sort"
1521
- >
1522
- <div
1523
- style={{
1524
- display: 'flex',
1525
- flexDirection: 'column',
1526
- }}
1527
- // className="transform opacity-100 scale-100"
1528
- >
1529
- {/* <div
1530
- style={{
1531
- display: 'flex',
1532
- flexDirection: 'row',
1533
- alignItems: 'center',
1534
- }}
1535
- > */}
1536
- <div style={{ display: 'flex', flexDirection: 'column' }}>
1537
- <div
1538
- style={{
1539
- fontSize: '14px',
1540
- marginBottom: '6px',
1541
- fontWeight: '600',
1542
- color: theme.secondaryTextColor,
1543
- fontFamily: theme?.fontFamily,
1544
- }}
1545
- >
1546
- Column
1547
- </div>
1548
- {/* select column */}
1549
- <SelectComponent
1550
- value={selectedSortByColumn}
1551
- onChange={e => {
1552
- setSelectedSortByColumn(e);
1553
- }}
1554
- options={sortableColumns.map(elem => {
1555
- return { label: elem, value: elem };
1556
- })}
1557
- />
1558
- </div>
1559
- {/* </div> */}
1560
- {/* Select bucket (if date) */}
1561
-
1562
- {/* Select aggregations */}
1563
- <div
1564
- style={{
1565
- fontSize: '14px',
1566
- marginBottom: '6px',
1567
- fontWeight: '600',
1568
- color: theme.secondaryTextColor,
1569
- fontFamily: theme?.fontFamily,
1570
- marginTop: 20,
1571
- }}
1572
- >
1573
- Direction
1574
- </div>
1575
-
1576
- <SelectComponent
1577
- value={selectedSortByDirection}
1578
- onChange={e => {
1579
- setSelectedSortByDirection(e);
1580
- }}
1581
- options={[
1582
- { label: 'ascending', value: 'ascending' },
1583
- { label: 'descending', value: 'descending' },
1584
- ]}
1585
- />
1586
- <div style={{ height: 20 }} />
1587
- <ButtonComponent
1588
- id="custom-button"
1589
- onClick={() => {
1590
- if (sortByIndexBeingEdited > -1) {
1591
- updateSortBy(sortByIndexBeingEdited);
1592
- setIsOpen(false);
1593
- // close();
1594
- return;
1595
- }
1596
- addSortBy();
1597
- setIsOpen(false);
1598
- // close();
1599
- }}
1600
- label={sortByIndexBeingEdited > -1 ? 'Edit sort' : 'Add sort'}
1601
- />
1602
- </div>
1603
- </ModalComponent>
1604
- </div>
1605
- </div>
1606
- );
1607
- };
1608
-
1609
- const GroupByModal2 = ({
1610
- selectedGroupByColumn,
1611
- addGroupBy,
1612
- setSelectedGroupByColumn,
1613
- selectedTable,
1614
- groupByColumnType,
1615
- groupByIndexBeingEdited,
1616
- updateGroupBy,
1617
- groupBys,
1618
- removeGroupBy,
1619
- selectGroupBy,
1620
- SelectComponent,
1621
- ButtonComponent,
1622
- aggregations,
1623
- setAggregationColumn,
1624
- setAggregationType,
1625
- addAggregation,
1626
- dateBucket,
1627
- setDateBucket,
1628
- ModalComponent,
1629
- theme,
1630
- ModalTriggerComponent,
1631
- TextInputComponent,
1632
- tagStyle,
1633
- }) => {
1634
- const [isOpen, setIsOpen] = useState(false);
1635
- return (
1636
- <div style={{ display: 'flex', flexDirection: 'column' }}>
1637
- <div
1638
- style={{
1639
- position: 'relative',
1640
- display: 'inline-block',
1641
- textAlign: 'left',
1642
- }}
1643
- >
1644
- <div
1645
- style={{
1646
- display: 'flex',
1647
- flexDirection: 'row',
1648
- alignItems: 'center',
1649
- }}
1650
- >
1651
- <ModalTriggerComponent
1652
- onClick={() => setIsOpen(isOpen => !isOpen)}
1653
- label="Group by"
1654
- />
1655
- <div
1656
- style={{
1657
- overflowX: 'scroll',
1658
- display: 'flex',
1659
- flexDirection: 'row',
1660
- alignItems: 'center',
1661
- }}
1662
- >
1663
- {groupBys.map((elem, index) => (
1664
- <FilterTag
1665
- id="group-tag"
1666
- label={elem.tag}
1667
- removeFilter={removeGroupBy}
1668
- selectFilter={selectGroupBy}
1669
- setIsOpen={setIsOpen}
1670
- index={index}
1671
- theme={theme}
1672
- tagStyle={tagStyle}
1673
- key={'groupby' + index}
1674
- />
1675
- ))}
1676
- </div>
1677
- </div>
1678
- <ModalComponent
1679
- isOpen={isOpen}
1680
- onClose={() => setIsOpen(false)}
1681
- title="Add group by"
1682
- >
1683
- <div style={{ display: 'flex', flexDirection: 'column' }}>
1684
- <div style={{ display: 'flex', flexDirection: 'column' }}>
1685
- {/* select column */}
1686
- <div
1687
- style={{
1688
- fontSize: '14px',
1689
- marginBottom: '6px',
1690
- fontWeight: '600',
1691
- fontFamily: theme?.fontFamily,
1692
- color: theme.secondaryTextColor,
1693
- }}
1694
- >
1695
- Column
1696
- </div>
1697
- <SelectComponent
1698
- label="Column"
1699
- value={selectedGroupByColumn.name}
1700
- onChange={e => {
1701
- const column = selectedTable.columns.find(c => c.name === e);
1702
- setSelectedGroupByColumn(column);
1703
- }}
1704
- options={selectedTable.columns.map(elem => {
1705
- return { label: elem.name, value: elem.name };
1706
- })}
1707
- />
1708
- </div>
1709
- {groupByColumnType === 'date' && (
1710
- <div
1711
- style={{
1712
- display: 'flex',
1713
- flexDirection: 'column',
1714
- // marginLeft: 12,
1715
- }}
1716
- >
1717
- <div
1718
- style={{
1719
- fontSize: '14px',
1720
- marginBottom: '6px',
1721
- fontWeight: '600',
1722
- color: theme.secondaryTextColor,
1723
- fontFamily: theme?.fontFamily,
1724
- marginTop: 20,
1725
- }}
1726
- >
1727
- Bucket
1728
- </div>
1729
- <SelectComponent
1730
- label="Bucket"
1731
- value={dateBucket}
1732
- onChange={e => {
1733
- setDateBucket(e);
1734
- }}
1735
- options={[
1736
- { label: 'month', value: 'month' },
1737
- { label: 'day', value: 'day' },
1738
- { label: 'week', value: 'week' },
1739
- ]}
1740
- />
1741
- </div>
1742
- )}
1743
- {/* Select bucket (if date) */}
1744
-
1745
- {/* Select aggregations */}
1746
- <div
1747
- style={{
1748
- fontSize: 14,
1749
- marginBottom: '6px',
1750
- fontWeight: '600',
1751
- marginTop: 20,
1752
- color: theme.secondaryTextColor,
1753
- fontFamily: theme?.fontFamily,
1754
- }}
1755
- >
1756
- Aggregations
1757
- </div>
1758
- {/* select column */}
1759
- {aggregations.map((aggregation, index) => (
1760
- // setAggregationType
1761
- <div
1762
- key={'agg' + index}
1763
- style={{
1764
- display: 'flex',
1765
- flexDirection: 'row',
1766
- alignItems: 'center',
1767
- }}
1768
- >
1769
- <SelectComponent
1770
- value={aggregation.column?.name}
1771
- onChange={e => {
1772
- const column = selectedTable.columns.find(
1773
- c => c.name === e
1774
- );
1775
- setAggregationColumn(column, index);
1776
- }}
1777
- options={selectedTable.columns.map(elem => {
1778
- return { label: elem.name, value: elem.name };
1779
- })}
1780
- />
1781
- <div style={{ width: 16 }} />
1782
- <SelectComponent
1783
- value={aggregation.aggregationType}
1784
- onChange={e => {
1785
- setAggregationType(e, index);
1786
- }}
1787
- options={[
1788
- { label: 'sum', value: 'sum' },
1789
- { label: 'average', value: 'average' },
1790
- { label: 'count', value: 'count' },
1791
- ]}
1792
- />
1793
- </div>
1794
- ))}
1795
- <div style={{ height: 20 }} />
1796
- <ButtonComponent
1797
- id="custom-button"
1798
- onClick={() => {
1799
- if (groupByIndexBeingEdited > -1) {
1800
- updateGroupBy(groupByIndexBeingEdited);
1801
- setIsOpen(false);
1802
- // close();
1803
- return;
1804
- }
1805
- addGroupBy();
1806
- setIsOpen(false);
1807
- // close();
1808
- }}
1809
- label={
1810
- groupByIndexBeingEdited > -1 ? 'Edit group by' : 'Add group by'
1811
- }
1812
- />
1813
- </div>
1814
- </ModalComponent>
1815
- </div>
1816
- </div>
1817
- );
1818
- };
1819
-
1820
- const AddFilterModal2 = ({
1821
- filters,
1822
- selectedColumn,
1823
- numberStart,
1824
- numberEnd,
1825
- dateStart,
1826
- setDateStart,
1827
- columnStats,
1828
- stringFilterValues,
1829
- setStringFilterValues,
1830
- addFilter,
1831
- setSelectedColumn,
1832
- setNumberStart,
1833
- setNumberEnd,
1834
- selectedTable,
1835
- columnType,
1836
- setDateEnd,
1837
- dateEnd,
1838
- removeFilter,
1839
- selectFilter,
1840
- indexBeingEdited,
1841
- updateFilter,
1842
- SelectComponent,
1843
- ButtonComponent,
1844
- ModalComponent,
1845
- theme,
1846
- ModalTriggerComponent,
1847
- TextInputComponent,
1848
- tagStyle,
1849
- }) => {
1850
- const [isOpen, setIsOpen] = useState(false);
1851
-
1852
- return (
1853
- <div style={{ display: 'flex', flexDirection: 'column' }}>
1854
- <div
1855
- style={{
1856
- position: 'relative',
1857
- display: 'inline-block',
1858
- textAlign: 'left',
1859
- }}
1860
- >
1861
- <div
1862
- style={{
1863
- display: 'flex',
1864
- flexDirection: 'row',
1865
- alignItems: 'center',
1866
- }}
1867
- >
1868
- <ModalTriggerComponent
1869
- onClick={() => setIsOpen(isOpen => !isOpen)}
1870
- label="Filter"
1871
- />
1872
- <div
1873
- style={{
1874
- overflowX: 'scroll',
1875
- display: 'flex',
1876
- flexDirection: 'row',
1877
- alignItems: 'center',
1878
- }}
1879
- >
1880
- {filters.map((elem, index) => (
1881
- <FilterTag
1882
- id="filter-tag"
1883
- label={elem.tag}
1884
- removeFilter={removeFilter}
1885
- selectFilter={selectFilter}
1886
- index={index}
1887
- theme={theme}
1888
- setIsOpen={setIsOpen}
1889
- key={'filter' + index}
1890
- tagStyle={tagStyle}
1891
- />
1892
- ))}
1893
- </div>
1894
- </div>
1895
- <ModalComponent
1896
- isOpen={isOpen}
1897
- onClose={() => setIsOpen(false)}
1898
- title="Add filter"
1899
- >
1900
- <div
1901
- style={{
1902
- backgroundColor: 'rgb(255, 255, 255)',
1903
- display: 'flex',
1904
- flexDirection: 'column',
1905
- }}
1906
- >
1907
- <div
1908
- style={{
1909
- fontSize: 14,
1910
- marginBottom: '6px',
1911
- fontWeight: '600',
1912
- color: theme.secondaryTextColor,
1913
- fontFamily: theme?.fontFamily,
1914
- }}
1915
- >
1916
- Column
1917
- </div>
1918
- <SelectComponent
1919
- id="custom-select"
1920
- value={selectedColumn.name}
1921
- onChange={e => {
1922
- const column = selectedTable.columns.find(c => c.name === e);
1923
- setSelectedColumn(column);
1924
- }}
1925
- options={selectedTable.columns.map(elem => {
1926
- return { label: elem.name, value: elem.name };
1927
- })}
1928
- />
1929
-
1930
- {columnType === 'number' && (
1931
- <div>
1932
- <div
1933
- style={{
1934
- display: 'flex',
1935
- flexDirection: 'row',
1936
- alignItems: 'center',
1937
- justifyContent: 'space-between',
1938
- }}
1939
- >
1940
- <div
1941
- style={{
1942
- display: 'flex',
1943
- flexDirection: 'column',
1944
- marginTop: '20px',
1945
- }}
1946
- >
1947
- <div
1948
- style={{
1949
- fontSize: '14px',
1950
- fontWeight: '600',
1951
- color: theme.secondaryTextColor,
1952
- fontFamily: theme?.fontFamily,
1953
- marginBottom: 6,
1954
- }}
1955
- >
1956
- Minimum
1957
- </div>
1958
- <TextInputComponent
1959
- id="min-input"
1960
- value={numberStart}
1961
- onChange={e => setNumberStart(e.target.value)}
1962
- />
1963
- </div>
1964
- <div style={{ width: 16 }} />
1965
- <div
1966
- style={{
1967
- display: 'flex',
1968
- flexDirection: 'column',
1969
- marginTop: '20px',
1970
- }}
1971
- >
1972
- <div
1973
- style={{
1974
- fontSize: '14px',
1975
- fontWeight: '600',
1976
- color: theme.secondaryTextColor,
1977
- fontFamily: theme?.fontFamily,
1978
- marginBottom: 6,
1979
- }}
1980
- >
1981
- Maximum
1982
- </div>
1983
- <TextInputComponent
1984
- id="max-input"
1985
- value={numberEnd}
1986
- onChange={e => setNumberEnd(e.target.value)}
1987
- />
1988
- </div>
1989
- </div>
1990
- </div>
1991
- )}
1992
-
1993
- {columnType === 'date' && (
1994
- <div
1995
- style={{
1996
- display: 'flex',
1997
- flexDirection: 'row',
1998
- alignItems: 'center',
1999
- justifyContent: 'space-between',
2000
- }}
2001
- >
2002
- <div
2003
- style={{
2004
- display: 'flex',
2005
- flexDirection: 'column',
2006
- marginTop: '20px',
2007
- }}
2008
- >
2009
- <div
2010
- style={{
2011
- fontSize: 14,
2012
- fontWeight: '600',
2013
- color: 'rgba(56, 65, 81, 0.7)',
2014
- fontFamily: theme?.fontFamily,
2015
- }}
2016
- >
2017
- Start date
2018
- </div>
2019
- <input
2020
- type="date"
2021
- value={dateStart}
2022
- onChange={e => setDateStart(e.target.value)}
2023
- placeholder="Start date"
2024
- // style={{
2025
- // width: '115px',
2026
- // fontSize: '0.8rem',
2027
- // color: theme?.primaryTextColor,
2028
- // borderWidth: '1px',
2029
- // marginTop: '4px',
2030
- // borderColor: '#E7E7E7',
2031
- // backgroundColor: 'white',
2032
- // borderRadius: '0.375rem',
2033
- // boxShadow: '0 1px 2px 0 rgba(0, 0, 0, 0.05)',
2034
- // paddingLeft: '0.5rem',
2035
- // paddingRight: '0.5rem',
2036
- // paddingTop: '0.375rem',
2037
- // paddingBottom: '0.375rem',
2038
- // fontFamily: theme?.fontFamily,
2039
- // }}
2040
- />
2041
- </div>
2042
- <div
2043
- style={{
2044
- display: 'flex',
2045
- flexDirection: 'column',
2046
- marginTop: '20px',
2047
- }}
2048
- >
2049
- <div
2050
- style={{
2051
- fontSize: 14,
2052
- fontWeight: '600',
2053
- color: 'rgba(56, 65, 81, 0.7)',
2054
- fontFamily: theme?.fontFamily,
2055
- }}
2056
- >
2057
- End date
2058
- </div>
2059
- <input
2060
- type="date"
2061
- value={dateEnd}
2062
- onChange={e => setDateEnd(e.target.value)}
2063
- placeholder="End date"
2064
- // style={{
2065
- // width: '115px',
2066
- // fontSize: '0.8rem',
2067
- // color: theme?.primaryTextColor,
2068
- // borderWidth: '1px',
2069
- // marginTop: '4px',
2070
- // borderColor: '#E7E7E7',
2071
- // backgroundColor: 'white',
2072
- // borderRadius: '0.375rem',
2073
- // boxShadow: '0 1px 2px 0 rgba(0, 0, 0, 0.05)',
2074
- // paddingLeft: '0.5rem',
2075
- // paddingRight: '0.5rem',
2076
- // paddingTop: '0.375rem',
2077
- // paddingBottom: '0.375rem',
2078
- // fontFamily: theme?.fontFamily,
2079
- // }}
2080
- />
2081
- </div>
2082
- </div>
2083
- )}
2084
-
2085
- {columnType === 'string' &&
2086
- columnStats &&
2087
- columnStats.length > 0 && (
2088
- <div
2089
- style={{
2090
- flex: 'flex',
2091
- flexDirection: 'column',
2092
- marginTop: '14px',
2093
- overflow: 'hidden',
2094
- }}
2095
- >
2096
- {columnStats.map(value => (
2097
- <div
2098
- style={{
2099
- display: 'flex',
2100
- flexDirection: 'row',
2101
- alignItems: 'center',
2102
- }}
2103
- key={value}
2104
- >
2105
- <div
2106
- style={{
2107
- display: 'flex',
2108
- flexDirection: 'row',
2109
- alignItems: 'center',
2110
- paddingTop: 6,
2111
- paddingBottom: 6,
2112
- }}
2113
- key={value}
2114
- >
2115
- <DivCheckbox
2116
- theme={theme}
2117
- checked={stringFilterValues.includes(value)}
2118
- onChange={() => {
2119
- setStringFilterValues(prev =>
2120
- prev.includes(value)
2121
- ? prev.filter(v => v !== value)
2122
- : [...prev, value]
2123
- );
2124
- }}
2125
- />
2126
- <div
2127
- style={{
2128
- marginLeft: 6,
2129
- display: 'block',
2130
- overflow: 'hidden',
2131
- textOverflow: 'ellipsis',
2132
- whiteSpace: 'nowrap',
2133
- color: theme?.primaryTextColor,
2134
- fontFamily: theme?.fontFamily,
2135
- }}
2136
- >
2137
- {value}
2138
- </div>
2139
- </div>
2140
- </div>
2141
- ))}
2142
- </div>
2143
- )}
2144
- <div style={{ height: 20 }} />
2145
- <ButtonComponent
2146
- id="custom-button"
2147
- onClick={() => {
2148
- if (indexBeingEdited > -1) {
2149
- updateFilter(indexBeingEdited);
2150
- setIsOpen(false);
2151
- return;
2152
- }
2153
- addFilter();
2154
- setIsOpen(false);
2155
- }}
2156
- label={indexBeingEdited > -1 ? 'Edit filter' : 'Add filter'}
2157
- />
2158
- </div>
2159
- </ModalComponent>
2160
- {/* )} */}
2161
- </div>
2162
- <div style={{ height: '12px' }}></div>
2163
- </div>
2164
- );
2165
- };
2166
-
2167
- const DivCheckbox = ({ onChange, checked, theme }) => {
2168
- const toggleCheckbox = () => {
2169
- if (onChange) {
2170
- onChange(!checked);
2171
- }
2172
- };
2173
-
2174
- const style = {
2175
- // display: 'inline-block',
2176
- width: '18px',
2177
- height: '18px',
2178
- background: checked ? '#384151' : '#fff',
2179
- border: checked ? '1px solid #384151' : '1px solid #E7E7E7',
2180
- borderRadius: '4px',
2181
- position: 'relative',
2182
- cursor: 'pointer',
2183
- boxShadow: '0 1px 2px 0 rgba(0, 0, 0, 0.05)',
2184
- fontFamily: theme?.fontFamily,
2185
- display: 'flex',
2186
- flexDirection: 'column',
2187
- alignItems: 'center',
2188
- justifyContent: 'center',
2189
- };
2190
-
2191
- return (
2192
- <div
2193
- style={style}
2194
- onClick={toggleCheckbox}
2195
- aria-checked={checked}
2196
- // className="shadow-sm"
2197
- role="checkbox"
2198
- >
2199
- {checked && (
2200
- <CheckIcon
2201
- style={{ color: theme?.backgroundColor, height: 16, width: 16 }}
2202
- className="text-white"
2203
- aria-hidden="true"
2204
- />
2205
- )}
2206
- </div>
2207
- );
2208
- };