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