@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,860 @@
1
+ import React, { useState, useContext, useMemo, useEffect } from 'react';
2
+ import MonacoEditor from '@monaco-editor/react';
3
+ // import './nightOwlLight.css';
4
+ import axios from 'axios';
5
+ import { TailSpin } from 'react-loader-spinner';
6
+ import { ClientContext, SchemaContext, ThemeContext } from './Context';
7
+ import {
8
+ ChevronDownIcon,
9
+ ChevronRightIcon,
10
+ SparklesIcon,
11
+ MagnifyingGlassIcon,
12
+ } from '@heroicons/react/20/solid';
13
+
14
+ export function convertPostgresColumn(column) {
15
+ let format;
16
+
17
+ switch (column.dataTypeID) {
18
+ case 20: // int8
19
+ case 21: // int2
20
+ case 23: // int4
21
+ format = 'whole_number';
22
+ break;
23
+ case 700: // float4
24
+ case 701: // float8
25
+ case 1700: // numeric
26
+ format = 'two_decimal_places';
27
+ break;
28
+ case 1082: // date
29
+ case 1083: // time
30
+ case 1184: // timestamptz
31
+ case 1114: // timestamp
32
+ format = 'MMM_dd_yyyy';
33
+ break;
34
+ case 1043: // varchar
35
+ default:
36
+ format = 'string';
37
+ }
38
+
39
+ return {
40
+ label: column.name,
41
+ field: column.name,
42
+ format: format,
43
+ };
44
+ }
45
+
46
+ function defineEditorTheme(monaco, theme) {
47
+ monaco.editor.defineTheme('onedark', {
48
+ base: theme.darkMode ? 'vs-dark' : 'vs',
49
+ inherit: true,
50
+ rules: [
51
+ {
52
+ token: 'comment',
53
+ foreground: '#5d7988',
54
+ fontStyle: 'italic',
55
+ },
56
+ { token: 'constant', foreground: '#e06c75' },
57
+ ],
58
+ colors: {
59
+ 'editor.background': '#F9F9F9',
60
+ },
61
+ });
62
+ }
63
+ function setEditorTheme(editor, monaco) {
64
+ try {
65
+ monaco.editor.setTheme('onedark');
66
+ } catch (e) {
67
+ console.log('ERROR: ', e);
68
+ }
69
+ }
70
+
71
+ interface SQLEditorProps {
72
+ containerStyle: React.CSSProperties;
73
+ }
74
+
75
+ export default function QueryEditor({
76
+ containerStyle = { height: '100vh' },
77
+ }: SQLEditorProps) {
78
+ const [sqlPrompt, setSqlPrompt] = useState('');
79
+
80
+ const [isOpen, setIsOpen] = useState(false);
81
+
82
+ const [client] = useContext(ClientContext);
83
+ const [theme] = useContext(ThemeContext);
84
+ const [query, setQuery] = useState('');
85
+ const [rows, setRows] = useState([]);
86
+ const [columns, setColumns] = useState([]);
87
+ const [fields, setFields] = useState([]);
88
+ const [schema, setSchema] = useContext(SchemaContext);
89
+ const [errorMessage, setErrorMessage] = useState('');
90
+ const [sqlResponseLoading, setSqlResponseLoading] = useState(false);
91
+
92
+ useEffect(() => {
93
+ let isSubscribed = true;
94
+ async function getSchema() {
95
+ const { publicKey, environment } = client;
96
+ const response3 = await axios.get(
97
+ `https://quill-344421.uc.r.appspot.com/schema2/${publicKey}/`,
98
+ {
99
+ headers: {
100
+ Authorization: `Bearer `,
101
+ environment: environment || undefined,
102
+ },
103
+ }
104
+ );
105
+ if (isSubscribed) {
106
+ setSchema(response3.data.tables);
107
+ }
108
+ }
109
+ if (isSubscribed) {
110
+ getSchema();
111
+ }
112
+ return () => {
113
+ isSubscribed = false;
114
+ };
115
+ }, []);
116
+
117
+ const handleRunSqlPrompt = async () => {
118
+ const { publicKey, environment } = client;
119
+ setSqlResponseLoading(true);
120
+ const response = await axios.post(
121
+ `https://quill-344421.uc.r.appspot.com/quillai`,
122
+ {
123
+ initialQuestion: sqlPrompt,
124
+ publicKey: publicKey,
125
+ },
126
+ {
127
+ headers: {
128
+ Authorization: `Bearer `,
129
+ environment: environment || undefined,
130
+ },
131
+ }
132
+ );
133
+ setQuery(response.data.message);
134
+ setSqlResponseLoading(false);
135
+ };
136
+
137
+ const handleRunQuery = async () => {
138
+ const { publicKey, customerId, environment } = client;
139
+ try {
140
+ const response = await axios.post(
141
+ `https://quill-344421.uc.r.appspot.com/dashquery`,
142
+ {
143
+ query,
144
+ },
145
+ {
146
+ params: {
147
+ orgId: customerId,
148
+ publicKey: publicKey,
149
+ },
150
+ headers: {
151
+ Authorization: `Bearer `,
152
+ environment: environment || undefined,
153
+ },
154
+ }
155
+ );
156
+ if (response && response.data && response.data.errorMessage) {
157
+ setErrorMessage(
158
+ 'Failed to run SQL query: ' + response.data.errorMessage
159
+ );
160
+ setRows([]);
161
+ setColumns([]);
162
+ setFields([]);
163
+ return;
164
+ }
165
+ setErrorMessage('');
166
+ setRows(response.data.rows);
167
+ setColumns(response.data.fields.map(elem => convertPostgresColumn(elem)));
168
+ setFields(response.data.fields);
169
+ } catch (e) {
170
+ console.log('ERROR: ', e);
171
+ return;
172
+ }
173
+ };
174
+
175
+ const handleAddToDashboard = () => {
176
+ setIsOpen(true);
177
+ };
178
+
179
+ const newRows = useMemo(() => {
180
+ return JSON.parse(JSON.stringify(rows));
181
+ }, [rows]);
182
+
183
+ /* all your useState and useContext calls and your useEffect hooks */
184
+
185
+ const downloadCSV = () => {
186
+ // report.rows
187
+ if (!rows.length) {
188
+ return;
189
+ }
190
+ const json = rows; // JSON data passed as a prop
191
+ const fields = Object.keys(json[0]); // Assumes all objects have same keys
192
+ const csvRows = [];
193
+
194
+ // Header row
195
+ csvRows.push(fields.join(','));
196
+
197
+ // Data rows
198
+ for (const row of json) {
199
+ const values = fields.map(field => JSON.stringify(row[field] || ''));
200
+ csvRows.push(values.join(','));
201
+ }
202
+
203
+ // Create CSV string and create a 'blob' with it
204
+ const csvString = csvRows.join('\r\n');
205
+ const csvBlob = new Blob([csvString], { type: 'text/csv' });
206
+
207
+ // Create a download link and click it
208
+ const downloadLink = document.createElement('a');
209
+ downloadLink.download = `${query}.csv`;
210
+ downloadLink.href = URL.createObjectURL(csvBlob);
211
+ downloadLink.style.display = 'none';
212
+
213
+ document.body.appendChild(downloadLink);
214
+ downloadLink.click();
215
+ document.body.removeChild(downloadLink);
216
+ };
217
+
218
+ /* rest of your methods */
219
+
220
+ return (
221
+ <div style={containerStyle}>
222
+ <div
223
+ style={{
224
+ height: 'calc(100%)',
225
+ display: 'flex',
226
+ flexDirection: 'column',
227
+ padding: 0,
228
+ }}
229
+ >
230
+ <div
231
+ style={{ height: '100%', display: 'flex', flexDirection: 'column' }}
232
+ >
233
+ <div
234
+ style={{
235
+ width: '100%',
236
+ display: 'flex',
237
+ height: '100%',
238
+ flexDirection: 'row',
239
+ }}
240
+ >
241
+ <SchemaListComponent schema={schema} theme={theme} />
242
+
243
+ <div
244
+ style={{
245
+ display: 'flex',
246
+ flexDirection: 'column',
247
+ width: 'calc(100% - 250px)',
248
+ }}
249
+ >
250
+ <div
251
+ style={{
252
+ // TODO: change color
253
+ height: 80,
254
+ background: 'white',
255
+ display: 'flex',
256
+ flexDirection: 'row',
257
+ alignItems: 'center',
258
+ }}
259
+ >
260
+ <div
261
+ style={{
262
+ display: 'flex',
263
+ flexDirection: 'row',
264
+ alignItems: 'center',
265
+ paddingLeft: '12px',
266
+ paddingRight: '12px',
267
+ height: 38,
268
+ boxShadow: 'rgba(0, 0, 0, 0.1) 0px 1px 5px 0px',
269
+ width: '445px',
270
+ // TODO: change color
271
+ borderColor: theme.borderColor,
272
+ color: theme.primaryTextColor,
273
+ borderWidth: '1px',
274
+ // TODO: change color
275
+ backgroundColor: 'white',
276
+ borderRadius: 6,
277
+ borderStyle: 'solid',
278
+ outline: 'none',
279
+ fontFamily: theme?.fontFamily,
280
+ }}
281
+ >
282
+ <MagnifyingGlassIcon
283
+ style={{
284
+ height: 16,
285
+ width: 16,
286
+ color: theme.secondaryTextColor,
287
+ }}
288
+ aria-hidden="true"
289
+ />
290
+ <input
291
+ value={sqlPrompt}
292
+ onChange={e => setSqlPrompt(e.target.value)}
293
+ style={{
294
+ outline: 'none',
295
+ marginLeft: 8,
296
+ width: '100%',
297
+ border: 'none',
298
+ outline: 'none',
299
+ fontFamily: theme?.fontFamily,
300
+ }}
301
+ />
302
+ </div>
303
+ <button
304
+ onClick={handleRunSqlPrompt}
305
+ style={{
306
+ // TODO: change color
307
+ background: theme.primaryButtonColor,
308
+ // TODO: change color
309
+ color: theme.backgroundColor,
310
+ height: 38,
311
+ paddingLeft: 10,
312
+ paddingRight: 12,
313
+ width: 87,
314
+ minWidth: 87,
315
+ maxWidth: 87,
316
+ marginLeft: 12,
317
+ fontWeight: '400',
318
+ borderRadius: '0.375rem',
319
+ border: 'none',
320
+ outline: 'none',
321
+ cursor: 'pointer',
322
+ fontFamily: theme?.fontFamily,
323
+ }}
324
+ >
325
+ {sqlResponseLoading ? (
326
+ <div
327
+ style={{
328
+ display: 'flex',
329
+ flexDirection: 'row',
330
+ alignItems: 'center',
331
+ width: '100%',
332
+ height: '100%',
333
+ justifyContent: 'center',
334
+ }}
335
+ >
336
+ <TailSpin
337
+ height="20"
338
+ width="20"
339
+ // TODO: change color
340
+ color={theme.backgroundColor}
341
+ ariaLabel="loading-indicator"
342
+ />
343
+ </div>
344
+ ) : (
345
+ <div
346
+ style={{
347
+ display: 'flex',
348
+ flexDirection: 'row',
349
+ alignItems: 'center',
350
+ }}
351
+ >
352
+ <SparklesIcon
353
+ style={{
354
+ height: 12,
355
+ width: 12,
356
+ marginRight: 4,
357
+ // TODO: change color
358
+ color: theme.backgroundColor,
359
+ }}
360
+ aria-hidden="true"
361
+ />
362
+ <div
363
+ style={{
364
+ fontSize: 14,
365
+ fontWeight: 600,
366
+ fontFamily: theme?.fontFamily,
367
+ color: theme.backgroundColor,
368
+ }}
369
+ >
370
+ Ask AI
371
+ </div>
372
+ </div>
373
+ )}
374
+ </button>
375
+ </div>
376
+ <div style={{ height: 'calc(50% - 40px)' }}>
377
+ <SQLEditorComponent
378
+ query={query}
379
+ setQuery={setQuery}
380
+ handleRunQuery={handleRunQuery}
381
+ theme={theme}
382
+ defineEditorTheme={defineEditorTheme}
383
+ setEditorTheme={setEditorTheme}
384
+ />
385
+ </div>
386
+ <div
387
+ style={{
388
+ height: 'calc(50% - 40px)',
389
+ display: 'flex',
390
+ flexDirection: 'column',
391
+ padding: 0,
392
+ margin: 0,
393
+ border: 'none',
394
+ outline: 'none',
395
+ }}
396
+ >
397
+ {errorMessage && (
398
+ <div
399
+ style={{
400
+ fontFamily: theme.fontFamily,
401
+ color: theme.primaryTextColor,
402
+ fontSize: 15,
403
+ fontWeight: '400',
404
+ }}
405
+ >
406
+ <div
407
+ style={{
408
+ padding: 30,
409
+ // TODO: change color
410
+ background: 'rgba(0,0,0,0.02)',
411
+ display: 'inline-block',
412
+ flex: 0,
413
+ borderRadius: 6,
414
+ fontFamily: theme?.fontFamily,
415
+ }}
416
+ >
417
+ {errorMessage}
418
+ </div>
419
+ </div>
420
+ )}
421
+ <div
422
+ style={{
423
+ height: 'calc(100% - 60px)',
424
+ overflow: 'scroll',
425
+ padding: 0,
426
+ margin: 0,
427
+ border: 'none',
428
+ outline: 'none',
429
+ }}
430
+ >
431
+ <table
432
+ style={{
433
+ padding: 0,
434
+ margin: 0,
435
+ border: 'none',
436
+ outline: 'none',
437
+ borderSpacing: 0,
438
+ }}
439
+ >
440
+ <thead
441
+ style={{
442
+ position: 'sticky',
443
+ // TODO: change color
444
+ background: theme.backgroundColor,
445
+ top: 0,
446
+ height: 37,
447
+ minHeight: 37,
448
+ maxHeight: 37,
449
+ }}
450
+ >
451
+ <tr
452
+ style={{
453
+ outline: 'none',
454
+ border: 'none',
455
+ padding: 0,
456
+ margin: 0,
457
+ }}
458
+ >
459
+ {columns.map((column, index) => (
460
+ <th
461
+ key={index}
462
+ scope="col"
463
+ style={{
464
+ textAlign: 'left',
465
+ fontFamily: theme?.fontFamily,
466
+ fontSize: 13,
467
+ height: 37,
468
+ minHeight: 37,
469
+ maxHeight: 37,
470
+ paddingLeft: 12,
471
+ paddingRight: 12,
472
+ paddingTop: 0,
473
+ paddingBottom: 0,
474
+ margin: 0,
475
+ fontWeight: '500',
476
+ // TODO: change color
477
+ boxShadow: `inset 0 -1px 0 ${theme.borderColor}`,
478
+ // TODO: change color
479
+ color: theme.primaryTextColor,
480
+ outline: 'none',
481
+ border: 'none',
482
+ }}
483
+ >
484
+ {column.label}
485
+ </th>
486
+ ))}
487
+ </tr>
488
+ </thead>
489
+ <tbody
490
+ style={{
491
+ // TODO: change color
492
+ backgroundColor: theme.backgroundColor,
493
+ }}
494
+ >
495
+ {newRows.map((row, rowIndex) => (
496
+ <tr key={rowIndex}>
497
+ {columns.map((column, columnIndex) => (
498
+ <td
499
+ key={columnIndex}
500
+ style={{
501
+ padding: 12,
502
+ whiteSpace: 'nowrap',
503
+ fontSize: 13,
504
+ color: theme.secondaryTextColor,
505
+ fontFamily: theme?.fontFamily,
506
+ // TODO: change color
507
+ // borderBottom: `1px solid ${theme.borderColor} !important`,
508
+ boxShadow: `inset 0 -1px 0 ${theme.borderColor}`,
509
+ outline: 'none',
510
+ }}
511
+ >
512
+ {typeof row[column.field] === 'object'
513
+ ? JSON.stringify(row[column.field]).length > 55
514
+ ? JSON.stringify(row[column.field]).substring(
515
+ 0,
516
+ 52
517
+ ) + '...'
518
+ : JSON.stringify(row[column.field])
519
+ : row[column.field].length > 55
520
+ ? row[column.field].substring(0, 52) + '...'
521
+ : row[column.field]}
522
+ </td>
523
+ ))}
524
+ </tr>
525
+ ))}
526
+ </tbody>
527
+ </table>
528
+ </div>
529
+ <div
530
+ style={{
531
+ height: 60,
532
+ // TODO: change color
533
+ background: theme.backgroundColor,
534
+ }}
535
+ >
536
+ {rows.length ? (
537
+ <div
538
+ style={{
539
+ display: 'flex',
540
+ flexDirection: 'column',
541
+ height: 60,
542
+ border: 'none',
543
+ }}
544
+ >
545
+ <div
546
+ style={{
547
+ height: 1,
548
+ // TODO: change color
549
+ background: theme.borderColor,
550
+ }}
551
+ />
552
+ <div style={{ marginLeft: 0 }}>
553
+ <button
554
+ type="button"
555
+ onClick={downloadCSV}
556
+ style={{
557
+ borderRadius: 6,
558
+ // TODO: change color
559
+ backgroundColor: theme.primaryButtonColor,
560
+ paddingLeft: '1rem',
561
+ paddingRight: '1rem',
562
+ paddingTop: '0.5rem',
563
+ paddingBottom: '0.5rem',
564
+ fontSize: '0.875rem',
565
+ fontWeight: '600',
566
+ // TODO: change color
567
+ color: theme.backgroundColor,
568
+ fontFamily: theme.fontFamily,
569
+ outline: 'none',
570
+ border: 'none',
571
+ cursor: 'pointer',
572
+ marginTop: 14,
573
+ }}
574
+ >
575
+ Download CSV
576
+ </button>
577
+ <div />
578
+ </div>
579
+ </div>
580
+ ) : null}
581
+ </div>
582
+ </div>
583
+ </div>
584
+ </div>
585
+ </div>
586
+ </div>
587
+ </div>
588
+ );
589
+ }
590
+
591
+ const SQLEditorComponent = ({
592
+ query,
593
+ setQuery,
594
+ handleRunQuery,
595
+ theme,
596
+ defineEditorTheme,
597
+ setEditorTheme,
598
+ }) => {
599
+ return (
600
+ <div
601
+ style={{
602
+ background: theme.backgroundColor,
603
+ // maxHeight: 700,
604
+ width: '100%',
605
+ height: '100%',
606
+ // minWidth: 450,
607
+ // overflowY: "scroll",
608
+ // padding: "20px 30px 20px 20px",
609
+ // marginLeft: 20,
610
+ borderTopLeftRadius: 6,
611
+ borderBottomLeftRadius: 6,
612
+ borderTopRightRadius: 0,
613
+ borderBottomRightRadius: 0,
614
+ overflow: 'hidden',
615
+ }}
616
+ >
617
+ <MonacoEditor
618
+ height="calc(100% - 50px)"
619
+ width="100%"
620
+ defaultLanguage="pgsql"
621
+ defaultValue=""
622
+ value={query}
623
+ loading={<div />}
624
+ options={{
625
+ wordWrap: 'on',
626
+ minimap: {
627
+ enabled: false,
628
+ },
629
+ padding: { top: 16 },
630
+ }}
631
+ onChange={query => setQuery(query)}
632
+ beforeMount={monaco => defineEditorTheme(monaco, theme)}
633
+ onMount={setEditorTheme}
634
+ />
635
+ <div
636
+ style={{
637
+ display: 'flex',
638
+ flexDirection: 'row',
639
+ alignItems: 'center',
640
+ height: 50,
641
+ }}
642
+ >
643
+ {/* <button
644
+ type="button"
645
+ onClick={handleRunQuery}
646
+ style={{
647
+ borderRadius: 8,
648
+ width: '100px',
649
+ // backgroundColor: "#212121",
650
+ height: '32px',
651
+ fontSize: 14,
652
+ fontWeight: '600',
653
+ backgroundColor: 'transparent',
654
+ color: '#384151',
655
+ border: 'none',
656
+ outline: 'none',
657
+ cursor: 'pointer',
658
+ }}
659
+ >
660
+ Run query
661
+ </button> */}
662
+ <button
663
+ type="button"
664
+ onClick={handleRunQuery}
665
+ style={{
666
+ borderRadius: 6,
667
+ backgroundColor: theme.primaryButtonColor,
668
+ height: 32,
669
+ width: 100,
670
+ marginLeft: 0,
671
+ // paddingLeft: '1rem',
672
+ // paddingRight: '1rem',
673
+ // paddingTop: '0.5rem',
674
+ // paddingBottom: '0.5rem',
675
+ fontSize: '0.875rem',
676
+ fontWeight: '600',
677
+ color: theme.backgroundColor,
678
+ fontFamily: theme.fontFamily,
679
+ outline: 'none',
680
+ border: 'none',
681
+ cursor: 'pointer',
682
+ }}
683
+ >
684
+ Run query
685
+ </button>
686
+ </div>
687
+ </div>
688
+ );
689
+ };
690
+
691
+ const SchemaListComponent = ({ schema, theme }) => {
692
+ return (
693
+ <div
694
+ style={{
695
+ background: theme.backgroundColor,
696
+ // maxHeight: 700,
697
+ width: 250,
698
+ minWidth: 250,
699
+ overflowY: 'scroll',
700
+ height: '100%',
701
+ // maxHeight: "100%",
702
+ paddingLeft: 20,
703
+ paddingRight: 30,
704
+ }}
705
+ >
706
+ {schema.map((elem, index) => (
707
+ <SchemaItem
708
+ key={elem.displayName + index}
709
+ elem={elem}
710
+ theme={theme}
711
+ index={index}
712
+ />
713
+ ))}
714
+ </div>
715
+ );
716
+ };
717
+
718
+ function SchemaItem({ elem, theme, index }) {
719
+ const [isOpen, setIsOpen] = useState(index === 0);
720
+
721
+ const schemaContainerStyle = {
722
+ display: 'flex',
723
+ flexDirection: 'column',
724
+ // WebkitTouchCallout: "none",
725
+ // WebkitUserSelect: "none",
726
+ // KhtmlUserSelect: "none",
727
+ // MozUserSelect: "none",
728
+ // msUserSelect: "none",
729
+ // userSelect: "none",
730
+ };
731
+
732
+ const schemaRowStyle = {
733
+ display: 'flex',
734
+ flexDirection: 'row',
735
+ alignItems: 'center',
736
+ width: '100%',
737
+ justifyContent: 'space-between',
738
+ cursor: 'pointer',
739
+ };
740
+
741
+ const schemaRowHoverStyle = {
742
+ background: theme.selectUnderlayColor,
743
+ };
744
+
745
+ return (
746
+ <div style={schemaContainerStyle}>
747
+ <div
748
+ style={{ ...schemaRowStyle, ...(isOpen && schemaRowHoverStyle) }}
749
+ onClick={() => setIsOpen(!isOpen)}
750
+ >
751
+ <p
752
+ style={{
753
+ marginLeft: theme.padding,
754
+ fontSize: 13,
755
+ color: '#384151',
756
+ fontWeight: '500',
757
+ whiteSpace: 'nowrap',
758
+ padding: 0,
759
+ margin: 0,
760
+ textOverflow: 'ellipsis',
761
+ overflow: 'hidden',
762
+ fontFamily: theme?.fontFamily,
763
+ }}
764
+ title={elem.displayName}
765
+ >
766
+ {elem.displayName}
767
+ </p>
768
+ <div
769
+ style={{
770
+ display: 'flex',
771
+ alignItems: 'center',
772
+ justifyContent: 'center',
773
+ // paddingRight: 25,
774
+ paddingTop: 12,
775
+ paddingBottom: 12,
776
+ paddingLeft: 0,
777
+ cursor: 'pointer',
778
+ }}
779
+ >
780
+ {/* <Arrow
781
+ fill={theme.fontColor}
782
+ style={{
783
+ transform: isOpen ? "scale(0.8) rotate(90deg)" : "scale(0.8)",
784
+ }}
785
+ /> */}
786
+ {isOpen ? (
787
+ <ChevronDownIcon
788
+ style={{ height: 13, width: 13, color: theme.secondaryTextColor }}
789
+ aria-hidden="true"
790
+ />
791
+ ) : (
792
+ <ChevronRightIcon
793
+ style={{ height: 13, width: 13, color: theme.secondaryTextColor }}
794
+ aria-hidden="true"
795
+ />
796
+ )}
797
+ </div>
798
+ </div>
799
+ {isOpen ? (
800
+ <div
801
+ style={{
802
+ paddingBottom: theme.padding,
803
+ display: 'flex',
804
+ flexDirection: 'column',
805
+ paddingLeft: theme.padding,
806
+ paddingRight: theme.padding,
807
+ }}
808
+ >
809
+ {elem.columns.map((elem, index) => (
810
+ <div
811
+ key={elem.displayName + elem.index}
812
+ style={{
813
+ paddingTop: theme.padding,
814
+ display: 'flex',
815
+ flexDirection: 'row',
816
+ alignItems: 'center',
817
+ justifyContent: 'space-between',
818
+ }}
819
+ >
820
+ <div
821
+ title={elem.displayName}
822
+ // className="text-gray-500"
823
+ style={{
824
+ fontSize: 12,
825
+ // color: theme.secondaryFontColor,
826
+ whiteSpace: 'nowrap',
827
+ color: theme.secondaryTextColor,
828
+ // color: "#6C727F",
829
+ padding: 0,
830
+ margin: 0,
831
+ textOverflow: 'ellipsis',
832
+ overflow: 'hidden',
833
+ width: 200,
834
+ maxWidth: 200,
835
+ fontFamily: theme?.fontFamily,
836
+ }}
837
+ >
838
+ {elem.displayName}
839
+ </div>
840
+ <div
841
+ // className="text-gray-500"
842
+ title={elem.displayName}
843
+ style={{
844
+ fontSize: 12,
845
+ // color: '#6C727F',
846
+ color: theme.secondaryTextColor,
847
+ padding: 0,
848
+ margin: 0,
849
+ fontFamily: theme?.fontFamily,
850
+ }}
851
+ >
852
+ {elem.fieldType}
853
+ </div>
854
+ </div>
855
+ ))}
856
+ </div>
857
+ ) : null}
858
+ </div>
859
+ );
860
+ }