@peak-ai/canvas 1.4.22 → 1.4.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +7 -45
- package/.babelrc +0 -14
- package/.eslintcache +0 -1
- package/.eslintignore +0 -5
- package/.eslintrc.js +0 -29
- package/dist/package.json +0 -62
- package/scripts/build.ts +0 -120
- package/src/GrapesjsCanvas.tsx +0 -494
- package/src/constants/index.ts +0 -25
- package/src/declaration.d.ts +0 -1
- package/src/helpers/compiled-table.css +0 -2429
- package/src/helpers/css.ts +0 -2667
- package/src/helpers/date-picker.ts +0 -807
- package/src/helpers/filter-placeholder.ts +0 -18
- package/src/helpers/index.ts +0 -13
- package/src/helpers/merge-json.ts +0 -106
- package/src/index.styles.ts +0 -58
- package/src/index.ts +0 -9
- package/src/plugins/grapejs-plugin.tsx +0 -196
- package/src/plugins/helpers/custom-modal.tsx +0 -123
- package/src/plugins/helpers/data-table.tsx +0 -300
- package/src/plugins/helpers/extra.tsx +0 -164
- package/src/plugins/helpers/query-cache-context.tsx +0 -154
- package/src/plugins/helpers/query-cache-singleton.ts +0 -176
- package/src/plugins/helpers/query-cache-utils.ts +0 -226
- package/src/plugins/helpers/query-details-modal.tsx +0 -400
- package/src/plugins/helpers/query-heading-formatter.ts +0 -24
- package/src/plugins/helpers/query-loading-modal.tsx +0 -94
- package/src/plugins/helpers/render-components.tsx +0 -1450
- package/src/plugins/helpers/styled-info-button.tsx +0 -504
- package/src/public/canvas.css +0 -42
- package/src/public/components-css/table/table-output.css +0 -2436
- package/src/public/components-css/table/table.css +0 -30
- package/src/public/output.css +0 -2465
- package/src/public/table.css +0 -135
- package/src/shadcn/components/icons/AiAvatarIcon.tsx +0 -47
- package/src/shadcn/components/icons/Co_driver Expanding button copy.svg +0 -21
- package/src/shadcn/components/icons/ai-avatar.svg +0 -7
- package/src/shadcn/components/icons/thinking.gif +0 -0
- package/src/shadcn/components/ui/button.tsx +0 -132
- package/src/shadcn/components/ui/card.tsx +0 -92
- package/src/shadcn/components/ui/chart.tsx +0 -324
- package/src/shadcn/components/ui/checkbox.tsx +0 -27
- package/src/shadcn/components/ui/component-wrapper.tsx +0 -61
- package/src/shadcn/components/ui/date-filter.tsx +0 -816
- package/src/shadcn/components/ui/error-container.tsx +0 -125
- package/src/shadcn/components/ui/error-wrapper.tsx +0 -99
- package/src/shadcn/components/ui/filter.tsx +0 -368
- package/src/shadcn/components/ui/hover-card.tsx +0 -36
- package/src/shadcn/components/ui/input.tsx +0 -20
- package/src/shadcn/components/ui/label.tsx +0 -24
- package/src/shadcn/components/ui/pagination.tsx +0 -213
- package/src/shadcn/components/ui/scroll-area.tsx +0 -59
- package/src/shadcn/components/ui/search.tsx +0 -150
- package/src/shadcn/components/ui/separator.tsx +0 -26
- package/src/shadcn/components/ui/skeleton.tsx +0 -69
- package/src/shadcn/components/ui/table.tsx +0 -196
- package/src/shadcn/components/ui/tabs.tsx +0 -55
- package/src/shadcn/components/ui/textarea.tsx +0 -18
- package/src/shadcn/components/ui/tooltip.tsx +0 -87
- package/src/shadcn/utils.ts +0 -6
- package/src/types/grapesjs-tailwind.d.ts +0 -61
- package/src/types/images.d.ts +0 -1
- package/tailwind.config.js +0 -5
- package/tooling/tailwind-compiler/index.js +0 -99
- package/tooling/tailwind-compiler/package.json +0 -11
- package/tooling/tailwind-compiler/yarn.lock +0 -123
- package/tsconfig.build.json +0 -15
- package/tsconfig.json +0 -8
- /package/{dist/GrapesjsCanvas.d.ts → GrapesjsCanvas.d.ts} +0 -0
- /package/{dist/GrapesjsCanvas.js → GrapesjsCanvas.js} +0 -0
- /package/{dist/GrapesjsCanvas.js.map → GrapesjsCanvas.js.map} +0 -0
- /package/{dist/constants → constants}/index.d.ts +0 -0
- /package/{dist/constants → constants}/index.js +0 -0
- /package/{dist/constants → constants}/index.js.map +0 -0
- /package/{dist/declaration.d.js → declaration.d.js} +0 -0
- /package/{dist/declaration.d.js.map → declaration.d.js.map} +0 -0
- /package/{dist/helpers → helpers}/compiled-table.css +0 -0
- /package/{dist/helpers → helpers}/css.d.ts +0 -0
- /package/{dist/helpers → helpers}/css.js +0 -0
- /package/{dist/helpers → helpers}/css.js.map +0 -0
- /package/{dist/helpers → helpers}/date-picker.d.ts +0 -0
- /package/{dist/helpers → helpers}/date-picker.js +0 -0
- /package/{dist/helpers → helpers}/date-picker.js.map +0 -0
- /package/{dist/helpers → helpers}/filter-placeholder.d.ts +0 -0
- /package/{dist/helpers → helpers}/filter-placeholder.js +0 -0
- /package/{dist/helpers → helpers}/filter-placeholder.js.map +0 -0
- /package/{dist/helpers → helpers}/index.d.ts +0 -0
- /package/{dist/helpers → helpers}/index.js +0 -0
- /package/{dist/helpers → helpers}/index.js.map +0 -0
- /package/{dist/helpers → helpers}/merge-json.d.ts +0 -0
- /package/{dist/helpers → helpers}/merge-json.js +0 -0
- /package/{dist/helpers → helpers}/merge-json.js.map +0 -0
- /package/{dist/index.d.ts → index.d.ts} +0 -0
- /package/{dist/index.js → index.js} +0 -0
- /package/{dist/index.js.map → index.js.map} +0 -0
- /package/{dist/index.styles.d.ts → index.styles.d.ts} +0 -0
- /package/{dist/index.styles.js → index.styles.js} +0 -0
- /package/{dist/index.styles.js.map → index.styles.js.map} +0 -0
- /package/{dist/plugins → plugins}/grapejs-plugin.d.ts +0 -0
- /package/{dist/plugins → plugins}/grapejs-plugin.js +0 -0
- /package/{dist/plugins → plugins}/grapejs-plugin.js.map +0 -0
- /package/{dist/plugins → plugins}/helpers/custom-modal.d.ts +0 -0
- /package/{dist/plugins → plugins}/helpers/custom-modal.js +0 -0
- /package/{dist/plugins → plugins}/helpers/custom-modal.js.map +0 -0
- /package/{dist/plugins → plugins}/helpers/data-table.d.ts +0 -0
- /package/{dist/plugins → plugins}/helpers/data-table.js +0 -0
- /package/{dist/plugins → plugins}/helpers/data-table.js.map +0 -0
- /package/{dist/plugins → plugins}/helpers/extra.d.ts +0 -0
- /package/{dist/plugins → plugins}/helpers/extra.js +0 -0
- /package/{dist/plugins → plugins}/helpers/extra.js.map +0 -0
- /package/{dist/plugins → plugins}/helpers/query-cache-context.d.ts +0 -0
- /package/{dist/plugins → plugins}/helpers/query-cache-context.js +0 -0
- /package/{dist/plugins → plugins}/helpers/query-cache-context.js.map +0 -0
- /package/{dist/plugins → plugins}/helpers/query-cache-singleton.d.ts +0 -0
- /package/{dist/plugins → plugins}/helpers/query-cache-singleton.js +0 -0
- /package/{dist/plugins → plugins}/helpers/query-cache-singleton.js.map +0 -0
- /package/{dist/plugins → plugins}/helpers/query-cache-utils.d.ts +0 -0
- /package/{dist/plugins → plugins}/helpers/query-cache-utils.js +0 -0
- /package/{dist/plugins → plugins}/helpers/query-cache-utils.js.map +0 -0
- /package/{dist/plugins → plugins}/helpers/query-details-modal.d.ts +0 -0
- /package/{dist/plugins → plugins}/helpers/query-details-modal.js +0 -0
- /package/{dist/plugins → plugins}/helpers/query-details-modal.js.map +0 -0
- /package/{dist/plugins → plugins}/helpers/query-heading-formatter.d.ts +0 -0
- /package/{dist/plugins → plugins}/helpers/query-heading-formatter.js +0 -0
- /package/{dist/plugins → plugins}/helpers/query-heading-formatter.js.map +0 -0
- /package/{dist/plugins → plugins}/helpers/query-loading-modal.d.ts +0 -0
- /package/{dist/plugins → plugins}/helpers/query-loading-modal.js +0 -0
- /package/{dist/plugins → plugins}/helpers/query-loading-modal.js.map +0 -0
- /package/{dist/plugins → plugins}/helpers/render-components.d.ts +0 -0
- /package/{dist/plugins → plugins}/helpers/render-components.js +0 -0
- /package/{dist/plugins → plugins}/helpers/render-components.js.map +0 -0
- /package/{dist/plugins → plugins}/helpers/styled-info-button.d.ts +0 -0
- /package/{dist/plugins → plugins}/helpers/styled-info-button.js +0 -0
- /package/{dist/plugins → plugins}/helpers/styled-info-button.js.map +0 -0
- /package/{dist/shadcn → shadcn}/components/icons/AiAvatarIcon.d.ts +0 -0
- /package/{dist/shadcn → shadcn}/components/icons/AiAvatarIcon.js +0 -0
- /package/{dist/shadcn → shadcn}/components/icons/AiAvatarIcon.js.map +0 -0
- /package/{dist/shadcn → shadcn}/components/icons/thinking.gif +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/button.d.ts +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/button.js +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/button.js.map +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/card.d.ts +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/card.js +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/card.js.map +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/chart.d.ts +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/chart.js +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/chart.js.map +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/checkbox.d.ts +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/checkbox.js +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/checkbox.js.map +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/component-wrapper.d.ts +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/component-wrapper.js +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/component-wrapper.js.map +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/date-filter.d.ts +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/date-filter.js +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/date-filter.js.map +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/error-container.d.ts +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/error-container.js +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/error-container.js.map +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/error-wrapper.d.ts +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/error-wrapper.js +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/error-wrapper.js.map +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/filter.d.ts +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/filter.js +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/filter.js.map +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/hover-card.d.ts +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/hover-card.js +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/hover-card.js.map +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/input.d.ts +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/input.js +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/input.js.map +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/label.d.ts +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/label.js +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/label.js.map +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/pagination.d.ts +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/pagination.js +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/pagination.js.map +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/scroll-area.d.ts +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/scroll-area.js +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/scroll-area.js.map +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/search.d.ts +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/search.js +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/search.js.map +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/separator.d.ts +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/separator.js +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/separator.js.map +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/skeleton.d.ts +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/skeleton.js +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/skeleton.js.map +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/table.d.ts +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/table.js +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/table.js.map +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/tabs.d.ts +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/tabs.js +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/tabs.js.map +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/textarea.d.ts +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/textarea.js +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/textarea.js.map +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/tooltip.d.ts +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/tooltip.js +0 -0
- /package/{dist/shadcn → shadcn}/components/ui/tooltip.js.map +0 -0
- /package/{dist/shadcn → shadcn}/utils.d.ts +0 -0
- /package/{dist/shadcn → shadcn}/utils.js +0 -0
- /package/{dist/shadcn → shadcn}/utils.js.map +0 -0
- /package/{dist/types → types}/grapesjs-tailwind.d.js +0 -0
- /package/{dist/types → types}/grapesjs-tailwind.d.js.map +0 -0
- /package/{dist/types → types}/images.d.js +0 -0
- /package/{dist/types → types}/images.d.js.map +0 -0
|
@@ -1,1450 +0,0 @@
|
|
|
1
|
-
/* eslint-disable no-nested-ternary */
|
|
2
|
-
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
|
3
|
-
/* eslint-disable func-style */
|
|
4
|
-
/* eslint-disable @typescript-eslint/no-empty-function */
|
|
5
|
-
/* eslint-disable func-names */
|
|
6
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
7
|
-
import chroma from 'chroma-js';
|
|
8
|
-
import React, { useEffect, useState, JSX, useRef } from 'react';
|
|
9
|
-
import { renderNoDataFallback } from './extra';
|
|
10
|
-
import {
|
|
11
|
-
CircleAlert,
|
|
12
|
-
TrendingDown,
|
|
13
|
-
TrendingUp,
|
|
14
|
-
ShoppingCart,
|
|
15
|
-
ChartBarBig,
|
|
16
|
-
Tag,
|
|
17
|
-
MapPin,
|
|
18
|
-
} from 'lucide-react';
|
|
19
|
-
import { StyledInfoButton } from './styled-info-button';
|
|
20
|
-
import { filterPlaceholders } from '../../helpers/filter-placeholder';
|
|
21
|
-
|
|
22
|
-
import { theme } from '@peak-ai/ais-components/theme';
|
|
23
|
-
|
|
24
|
-
import Markdown from 'markdown-to-jsx';
|
|
25
|
-
|
|
26
|
-
import {
|
|
27
|
-
Bar,
|
|
28
|
-
BarChart,
|
|
29
|
-
CartesianGrid,
|
|
30
|
-
Legend,
|
|
31
|
-
Line,
|
|
32
|
-
LineChart,
|
|
33
|
-
Pie,
|
|
34
|
-
PieChart,
|
|
35
|
-
ResponsiveContainer,
|
|
36
|
-
XAxis,
|
|
37
|
-
YAxis,
|
|
38
|
-
} from 'recharts/lib';
|
|
39
|
-
|
|
40
|
-
// @ts-ignore
|
|
41
|
-
import * as domutil from 'recharts/lib/util/DOMUtils';
|
|
42
|
-
|
|
43
|
-
import { TooltipButton } from '../../shadcn/components/ui/button';
|
|
44
|
-
import {
|
|
45
|
-
Card,
|
|
46
|
-
CardContent,
|
|
47
|
-
CardDescription,
|
|
48
|
-
CardFooter,
|
|
49
|
-
CardHeader,
|
|
50
|
-
CardTitle,
|
|
51
|
-
} from '../../shadcn/components/ui/card';
|
|
52
|
-
import {
|
|
53
|
-
ChartContainer,
|
|
54
|
-
ChartTooltip,
|
|
55
|
-
ChartTooltipContent,
|
|
56
|
-
} from '../../shadcn/components/ui/chart';
|
|
57
|
-
import { renderFilter } from '../../shadcn/components/ui/filter';
|
|
58
|
-
import { ScrollArea, ScrollBar } from '../../shadcn/components/ui/scroll-area';
|
|
59
|
-
import { renderSearch } from '../../shadcn/components/ui/search';
|
|
60
|
-
import { CardLoader, ChartLoader, MarkdownLoader } from '../../shadcn/components/ui/skeleton';
|
|
61
|
-
import { SortDirection } from '../../shadcn/components/ui/table';
|
|
62
|
-
import { Tabs, TabsContent, TabsList, TabsTrigger } from '../../shadcn/components/ui/tabs';
|
|
63
|
-
|
|
64
|
-
import { cn } from '../../shadcn/utils';
|
|
65
|
-
import { getAffectedComponentsWithLoader } from '../../helpers';
|
|
66
|
-
import { DataTable } from './data-table';
|
|
67
|
-
import { ComponentWrapper } from '../../shadcn/components/ui/error-wrapper';
|
|
68
|
-
|
|
69
|
-
// Monkey-patching the getOffset function to use iframe's window instead of global one
|
|
70
|
-
// REMEMBER TO UDPATE THIS WHEN recharts IS UPGRADED
|
|
71
|
-
// @ts-ignore
|
|
72
|
-
domutil.getOffset = (
|
|
73
|
-
el: HTMLElement,
|
|
74
|
-
): {
|
|
75
|
-
top: number;
|
|
76
|
-
left: number;
|
|
77
|
-
} => {
|
|
78
|
-
const html = el.ownerDocument.documentElement;
|
|
79
|
-
let box = { top: 0, left: 0 };
|
|
80
|
-
|
|
81
|
-
if (typeof el.getBoundingClientRect !== 'undefined') {
|
|
82
|
-
box = el.getBoundingClientRect();
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const iframe = document.querySelector('.gjs-frame');
|
|
86
|
-
const iframeWindow = (iframe as any)?.contentWindow;
|
|
87
|
-
|
|
88
|
-
return {
|
|
89
|
-
top: box.top + iframeWindow.pageYOffset - html.clientTop,
|
|
90
|
-
left: box.left + iframeWindow.pageXOffset - html.clientLeft,
|
|
91
|
-
};
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
const baseColors = [
|
|
95
|
-
[theme.colors.Blue_100, theme.colors.Blue_30],
|
|
96
|
-
[theme.colors.Purpley_100, theme.colors.Purpley_30],
|
|
97
|
-
[theme.colors.Light_Bluish_Green, '#06601b'],
|
|
98
|
-
[theme.colors.Wild_Strawberry, '#4d001c'],
|
|
99
|
-
];
|
|
100
|
-
|
|
101
|
-
const iconMap: Record<string, JSX.Element> = {
|
|
102
|
-
TrendingUp: <TrendingUp />,
|
|
103
|
-
TrendingDown: <TrendingDown />,
|
|
104
|
-
ShoppingCart: <ShoppingCart />,
|
|
105
|
-
Equalizer: <ChartBarBig />,
|
|
106
|
-
Style: <Tag />,
|
|
107
|
-
Place: <MapPin />,
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
function getIconFromString(iconString: string): JSX.Element | null {
|
|
111
|
-
if (!iconString) {
|
|
112
|
-
return null;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
if (iconMap[iconString]) {
|
|
116
|
-
return iconMap[iconString];
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const lowerIconString = iconString.toLowerCase();
|
|
120
|
-
const matchingKey = Object.keys(iconMap).find((key) => key.toLowerCase() === lowerIconString);
|
|
121
|
-
|
|
122
|
-
if (matchingKey) {
|
|
123
|
-
return iconMap[matchingKey];
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return null;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
function getNColors(n: number) {
|
|
130
|
-
const colors: Array<string[]> = [];
|
|
131
|
-
const safeBaseColors = Array.isArray(baseColors) ? baseColors : [];
|
|
132
|
-
|
|
133
|
-
for (const baseColor of safeBaseColors) {
|
|
134
|
-
// eslint-disable-next-line import/no-named-as-default-member
|
|
135
|
-
colors.push(chroma.scale(baseColor).mode('lab').colors(n));
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function getOneColor(existing: Array<string>) {
|
|
139
|
-
let color: null | string = null;
|
|
140
|
-
|
|
141
|
-
do {
|
|
142
|
-
const base = colors[Math.floor(Math.random() * colors.length)];
|
|
143
|
-
const selectedColorIndex = Math.floor(Math.random() * base.length);
|
|
144
|
-
color = base[selectedColorIndex];
|
|
145
|
-
} while (existing.includes(color));
|
|
146
|
-
|
|
147
|
-
return color as string;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const selectedColors: Array<string> = [];
|
|
151
|
-
|
|
152
|
-
for (let i = 0; i < n; i++) {
|
|
153
|
-
selectedColors.push(getOneColor(selectedColors));
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
return selectedColors;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/* TODO: Loader remains */
|
|
160
|
-
export function renderActionCard(props: any): JSX.Element {
|
|
161
|
-
const { gjsModel, ...rest } = props;
|
|
162
|
-
|
|
163
|
-
const [allData, setAllData] = useState({
|
|
164
|
-
...filterPlaceholders(gjsModel.get('componentProps')),
|
|
165
|
-
...filterPlaceholders(props),
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
const {
|
|
169
|
-
headerContent = 'Default action card title',
|
|
170
|
-
icon,
|
|
171
|
-
bodyContent = 'Default body content. Click to edit.',
|
|
172
|
-
} = allData;
|
|
173
|
-
|
|
174
|
-
const [attributes, setAttributes] = useState({
|
|
175
|
-
...gjsModel.get('attributes'),
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
useEffect(() => {
|
|
179
|
-
gjsModel.on('change:componentProps', () => {
|
|
180
|
-
setAllData((prevData: any) => ({
|
|
181
|
-
...prevData,
|
|
182
|
-
...filterPlaceholders(gjsModel.get('componentProps')),
|
|
183
|
-
}));
|
|
184
|
-
});
|
|
185
|
-
}, []);
|
|
186
|
-
|
|
187
|
-
useEffect(() => {
|
|
188
|
-
gjsModel.on('change:attributes', () => {
|
|
189
|
-
setAttributes({
|
|
190
|
-
...gjsModel.get('attributes'),
|
|
191
|
-
});
|
|
192
|
-
});
|
|
193
|
-
}, []);
|
|
194
|
-
|
|
195
|
-
const defaultClasses = 'text-black p-4 rounded-lg shadow border flex flex-col h-full';
|
|
196
|
-
|
|
197
|
-
return (
|
|
198
|
-
<Card
|
|
199
|
-
className={`${defaultClasses} relative`}
|
|
200
|
-
style={{ backgroundColor: '#f9f9fe' }}
|
|
201
|
-
isEditable={false}
|
|
202
|
-
contentEditable={false}
|
|
203
|
-
{...rest}
|
|
204
|
-
>
|
|
205
|
-
<CardContent className="p-0" isEditable={false} contentEditable={false}>
|
|
206
|
-
<div className="px-4 py-3">
|
|
207
|
-
<div className="flex items-start flex-col">
|
|
208
|
-
<div className="flex items-center justify-between w-full">
|
|
209
|
-
<div className="text-xl font-semibold">{headerContent}</div>
|
|
210
|
-
<div className="text-5xl ml-2 flex items-center" aria-hidden="true">
|
|
211
|
-
{getIconFromString(icon)}
|
|
212
|
-
</div>
|
|
213
|
-
</div>
|
|
214
|
-
<div className="w-full mt-1">
|
|
215
|
-
{attributes.error ||
|
|
216
|
-
allData.error ||
|
|
217
|
-
attributes.isMissing ||
|
|
218
|
-
allData.isMissing ||
|
|
219
|
-
!bodyContent ? (
|
|
220
|
-
<span className="flex flex-row items-center px-3 py-2">
|
|
221
|
-
<CircleAlert className="w-3 h-3 text-gray-400 mr-1" />
|
|
222
|
-
<p className="text-sm text-gray-500 max-w-md text-center">
|
|
223
|
-
No data available for given filters
|
|
224
|
-
</p>
|
|
225
|
-
</span>
|
|
226
|
-
) : (
|
|
227
|
-
<div className="text-base leading-relaxed">{bodyContent}</div>
|
|
228
|
-
)}
|
|
229
|
-
</div>
|
|
230
|
-
</div>
|
|
231
|
-
</div>
|
|
232
|
-
</CardContent>
|
|
233
|
-
</Card>
|
|
234
|
-
);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
export function renderCard(props: any): JSX.Element {
|
|
238
|
-
const {
|
|
239
|
-
isEditable = false,
|
|
240
|
-
gjsModel,
|
|
241
|
-
performInteraction = () => Promise.resolve({}),
|
|
242
|
-
...rest
|
|
243
|
-
} = props;
|
|
244
|
-
|
|
245
|
-
const [allData, setAllData] = useState({
|
|
246
|
-
...filterPlaceholders(gjsModel.get('componentProps')),
|
|
247
|
-
...filterPlaceholders(props),
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
const {
|
|
251
|
-
headerContent,
|
|
252
|
-
headerDescription,
|
|
253
|
-
bodyContent,
|
|
254
|
-
footerContent,
|
|
255
|
-
footerClass,
|
|
256
|
-
footerIcon,
|
|
257
|
-
contentMetadata,
|
|
258
|
-
contentMetadataClass,
|
|
259
|
-
className: dataClassName,
|
|
260
|
-
error: dataError,
|
|
261
|
-
isMissing: dataIsMissing,
|
|
262
|
-
} = allData;
|
|
263
|
-
|
|
264
|
-
function toSafeText(value: unknown): string {
|
|
265
|
-
if (value === null) {
|
|
266
|
-
return '';
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
return typeof value === 'string' ? value : JSON.stringify(value);
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
const safeHeaderContent = toSafeText(headerContent);
|
|
273
|
-
const safeHeaderDescription = toSafeText(headerDescription);
|
|
274
|
-
const safeBodyContent = toSafeText(bodyContent);
|
|
275
|
-
const safeFooterContent = toSafeText(footerContent);
|
|
276
|
-
const safeContentMetadata = toSafeText(contentMetadata);
|
|
277
|
-
|
|
278
|
-
const [attributes, setAttributes] = useState({
|
|
279
|
-
...gjsModel.get('attributes'),
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
useEffect(() => {
|
|
283
|
-
gjsModel.on('change:componentProps', () => {
|
|
284
|
-
setAllData((prevData: any) => ({
|
|
285
|
-
...prevData,
|
|
286
|
-
...filterPlaceholders(gjsModel.get('componentProps')),
|
|
287
|
-
}));
|
|
288
|
-
});
|
|
289
|
-
}, []);
|
|
290
|
-
|
|
291
|
-
useEffect(() => {
|
|
292
|
-
gjsModel.on('change:attributes', () => {
|
|
293
|
-
setAttributes({
|
|
294
|
-
...gjsModel.get('attributes'),
|
|
295
|
-
});
|
|
296
|
-
});
|
|
297
|
-
}, []);
|
|
298
|
-
|
|
299
|
-
const defaultClasses =
|
|
300
|
-
'bg-white text-black p-4 rounded-lg border flex flex-col h-full shadow-none';
|
|
301
|
-
const defaultFooterClasses = safeFooterContent
|
|
302
|
-
? 'text-sm bg-pink-50 text-pink-900 shadow-sm'
|
|
303
|
-
: 'text-sm';
|
|
304
|
-
const defaultContentMetadataClasses = 'text-xs';
|
|
305
|
-
|
|
306
|
-
const mergedCardClassName = dataClassName ? cn(defaultClasses, dataClassName) : defaultClasses;
|
|
307
|
-
const mergedFooterClassName = footerClass
|
|
308
|
-
? cn(defaultFooterClasses, footerClass)
|
|
309
|
-
: defaultFooterClasses;
|
|
310
|
-
const mergedContentMetadataClassName = contentMetadataClass
|
|
311
|
-
? cn(defaultContentMetadataClasses, contentMetadataClass)
|
|
312
|
-
: defaultContentMetadataClasses;
|
|
313
|
-
|
|
314
|
-
const hasError = Boolean(attributes?.error ?? dataError);
|
|
315
|
-
const hasMissing = Boolean(attributes?.isMissing ?? dataIsMissing);
|
|
316
|
-
const isLoading = Boolean(
|
|
317
|
-
(attributes?.interactionApiInProgress || attributes?.loading) && !hasError && !hasMissing,
|
|
318
|
-
);
|
|
319
|
-
|
|
320
|
-
const footerIconText =
|
|
321
|
-
typeof footerIcon === 'string'
|
|
322
|
-
? footerIcon
|
|
323
|
-
: footerIcon && typeof footerIcon === 'object' && 'icon' in (footerIcon as any)
|
|
324
|
-
? String((footerIcon as any).icon)
|
|
325
|
-
: '';
|
|
326
|
-
|
|
327
|
-
function getCardContent() {
|
|
328
|
-
if (isLoading) {
|
|
329
|
-
return (
|
|
330
|
-
<CardContent>
|
|
331
|
-
<CardLoader />
|
|
332
|
-
</CardContent>
|
|
333
|
-
);
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
const showEmpty = hasError || hasMissing || !safeBodyContent;
|
|
337
|
-
|
|
338
|
-
return (
|
|
339
|
-
<React.Fragment>
|
|
340
|
-
<CardHeader>
|
|
341
|
-
<div className="flex items-start justify-between">
|
|
342
|
-
<div className="flex-1">
|
|
343
|
-
<CardTitle data-slot="headerContent" contentEditable={isEditable} className="text-lg">
|
|
344
|
-
{safeHeaderContent}
|
|
345
|
-
</CardTitle>
|
|
346
|
-
<CardDescription
|
|
347
|
-
data-slot="headerDescription"
|
|
348
|
-
contentEditable={isEditable}
|
|
349
|
-
className="text-sm"
|
|
350
|
-
>
|
|
351
|
-
{safeHeaderDescription}
|
|
352
|
-
</CardDescription>
|
|
353
|
-
</div>
|
|
354
|
-
</div>
|
|
355
|
-
</CardHeader>
|
|
356
|
-
|
|
357
|
-
{showEmpty ? (
|
|
358
|
-
<span className="flex flex-row items-center px-3 py-2">
|
|
359
|
-
<CircleAlert className="w-3 h-3 text-gray-400 mr-1" />
|
|
360
|
-
<p className="text-sm text-gray-500 max-w-md text-center">
|
|
361
|
-
No data available for given filters
|
|
362
|
-
</p>
|
|
363
|
-
</span>
|
|
364
|
-
) : (
|
|
365
|
-
<CardContent data-slot="bodyContent" contentEditable={false} className="flex-grow">
|
|
366
|
-
<div className="text-3xl font-semibold truncate">{safeBodyContent}</div>
|
|
367
|
-
{safeContentMetadata && (
|
|
368
|
-
<div className={mergedContentMetadataClassName} style={{ color: '#2A44D4' }}>
|
|
369
|
-
{safeContentMetadata}
|
|
370
|
-
</div>
|
|
371
|
-
)}
|
|
372
|
-
</CardContent>
|
|
373
|
-
)}
|
|
374
|
-
|
|
375
|
-
<CardFooter
|
|
376
|
-
data-slot="footerContent"
|
|
377
|
-
contentEditable={isEditable}
|
|
378
|
-
className={cn(mergedFooterClassName)}
|
|
379
|
-
>
|
|
380
|
-
{(footerIconText || '').trim() && (
|
|
381
|
-
<span className="mr-2" aria-hidden="true">
|
|
382
|
-
{footerIconText}
|
|
383
|
-
</span>
|
|
384
|
-
)}
|
|
385
|
-
{safeFooterContent || ''}
|
|
386
|
-
</CardFooter>
|
|
387
|
-
</React.Fragment>
|
|
388
|
-
);
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
return (
|
|
392
|
-
<Card {...rest} className={`${mergedCardClassName} relative`}>
|
|
393
|
-
{getCardContent()}
|
|
394
|
-
<StyledInfoButton
|
|
395
|
-
componentId={gjsModel.get('id')}
|
|
396
|
-
performInteraction={performInteraction}
|
|
397
|
-
position="top-right"
|
|
398
|
-
isVisible={isEditable}
|
|
399
|
-
componentProps={allData}
|
|
400
|
-
/>
|
|
401
|
-
</Card>
|
|
402
|
-
);
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
function renderChartComponent(chartType: string, data: any, config: any): JSX.Element {
|
|
406
|
-
const safeData = Array.isArray(data) ? data : [];
|
|
407
|
-
|
|
408
|
-
if (safeData.length === 0) {
|
|
409
|
-
return renderNoDataFallback();
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
switch (chartType) {
|
|
413
|
-
case 'pie':
|
|
414
|
-
return (
|
|
415
|
-
<PieChart>
|
|
416
|
-
{safeData.length >= 20 && (
|
|
417
|
-
<ChartTooltip cursor={false} content={<ChartTooltipContent hideLabel />} />
|
|
418
|
-
)}
|
|
419
|
-
<Pie
|
|
420
|
-
data={safeData}
|
|
421
|
-
dataKey={config.dataKey}
|
|
422
|
-
nameKey={config.nameKey}
|
|
423
|
-
innerRadius={60}
|
|
424
|
-
strokeWidth={10}
|
|
425
|
-
label={
|
|
426
|
-
safeData.length < 20
|
|
427
|
-
? ({ name, percent }: { name: string; percent: number }) =>
|
|
428
|
-
`${name}: ${(percent * 100).toFixed(0)}%`
|
|
429
|
-
: undefined
|
|
430
|
-
}
|
|
431
|
-
labelLine={false}
|
|
432
|
-
/>
|
|
433
|
-
</PieChart>
|
|
434
|
-
);
|
|
435
|
-
|
|
436
|
-
case 'bar': {
|
|
437
|
-
config.dataKeys = config.dataKeys || [];
|
|
438
|
-
|
|
439
|
-
const barDataKeys = Array.isArray(config.dataKeys) ? config.dataKeys : [];
|
|
440
|
-
|
|
441
|
-
return (
|
|
442
|
-
<BarChart data={safeData} {...config.chartUi}>
|
|
443
|
-
<CartesianGrid vertical={false} />
|
|
444
|
-
<XAxis dataKey={config.xAxis} />
|
|
445
|
-
<YAxis />
|
|
446
|
-
<Legend align="left" wrapperStyle={{ marginLeft: 20 }} />
|
|
447
|
-
{barDataKeys.map(({ key, name, color }: { key: string; name: string; color: string }) => {
|
|
448
|
-
return <Bar dataKey={key} fill={color ?? '#4caf50'} name={name} key={key} radius={2} />;
|
|
449
|
-
})}
|
|
450
|
-
<ChartTooltip cursor={false} content={<ChartTooltipContent indicator="dashed" />} />
|
|
451
|
-
</BarChart>
|
|
452
|
-
);
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
case 'line': {
|
|
456
|
-
config.dataKeys = config.dataKeys || [];
|
|
457
|
-
const safeDataKeysLine = Array.isArray(config.dataKeys) ? config.dataKeys : [];
|
|
458
|
-
|
|
459
|
-
return (
|
|
460
|
-
<LineChart data={safeData} {...config.chartUi}>
|
|
461
|
-
<CartesianGrid vertical={false} />
|
|
462
|
-
<XAxis dataKey={config.xAxis} />
|
|
463
|
-
<YAxis />
|
|
464
|
-
<ChartTooltip cursor={false} content={<ChartTooltipContent indicator="dashed" />} />
|
|
465
|
-
{safeDataKeysLine.map(
|
|
466
|
-
({ key, name, color }: { key: string; name: string; color: string }) => {
|
|
467
|
-
return (
|
|
468
|
-
<Line
|
|
469
|
-
type="monotone"
|
|
470
|
-
dataKey={key}
|
|
471
|
-
stroke={color ?? '#4caf50'}
|
|
472
|
-
name={name}
|
|
473
|
-
key={key}
|
|
474
|
-
/>
|
|
475
|
-
);
|
|
476
|
-
},
|
|
477
|
-
)}
|
|
478
|
-
<Legend align="left" />
|
|
479
|
-
</LineChart>
|
|
480
|
-
);
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
default:
|
|
484
|
-
return (
|
|
485
|
-
<PieChart>
|
|
486
|
-
{safeData.length >= 20 && (
|
|
487
|
-
<ChartTooltip cursor={false} content={<ChartTooltipContent hideLabel />} />
|
|
488
|
-
)}
|
|
489
|
-
<Pie
|
|
490
|
-
data={safeData}
|
|
491
|
-
dataKey={config.dataKey}
|
|
492
|
-
nameKey={config.nameKey}
|
|
493
|
-
innerRadius={60}
|
|
494
|
-
strokeWidth={10}
|
|
495
|
-
label={
|
|
496
|
-
safeData.length < 20
|
|
497
|
-
? ({ name, percent }: { name: string; percent: number }) =>
|
|
498
|
-
`${name}: ${(percent * 100).toFixed(0)}%`
|
|
499
|
-
: undefined
|
|
500
|
-
}
|
|
501
|
-
labelLine={false}
|
|
502
|
-
/>
|
|
503
|
-
</PieChart>
|
|
504
|
-
);
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
export function renderChart(props: any): JSX.Element {
|
|
509
|
-
const {
|
|
510
|
-
isEditable = false,
|
|
511
|
-
gjsModel,
|
|
512
|
-
performInteraction = () => Promise.resolve({}),
|
|
513
|
-
...rest
|
|
514
|
-
} = props;
|
|
515
|
-
|
|
516
|
-
const [allData, setAllData] = useState({
|
|
517
|
-
...filterPlaceholders(gjsModel.get('componentProps')),
|
|
518
|
-
...filterPlaceholders(props),
|
|
519
|
-
});
|
|
520
|
-
|
|
521
|
-
const [attributes, setAttributes] = useState({
|
|
522
|
-
...gjsModel.get('attributes'),
|
|
523
|
-
});
|
|
524
|
-
|
|
525
|
-
const containerRef = useRef<HTMLDivElement>(null);
|
|
526
|
-
const [containerWidth, setContainerWidth] = useState<number>(0);
|
|
527
|
-
|
|
528
|
-
useEffect(() => {
|
|
529
|
-
const handlePropsChange = () => {
|
|
530
|
-
const newProps = filterPlaceholders(gjsModel.get('componentProps'));
|
|
531
|
-
setAllData((prevData: any) => ({
|
|
532
|
-
...prevData, // Preserve existing data (including __sql_query_params)
|
|
533
|
-
...newProps,
|
|
534
|
-
}));
|
|
535
|
-
};
|
|
536
|
-
|
|
537
|
-
gjsModel.on('change:componentProps', handlePropsChange);
|
|
538
|
-
|
|
539
|
-
return () => {
|
|
540
|
-
gjsModel.off('change:componentProps', handlePropsChange);
|
|
541
|
-
};
|
|
542
|
-
}, [gjsModel]);
|
|
543
|
-
|
|
544
|
-
useEffect(() => {
|
|
545
|
-
const handleAttributesChange = () => {
|
|
546
|
-
const newAttributes = gjsModel.get('attributes');
|
|
547
|
-
setAttributes({
|
|
548
|
-
...newAttributes,
|
|
549
|
-
});
|
|
550
|
-
};
|
|
551
|
-
|
|
552
|
-
gjsModel.on('change:attributes', handleAttributesChange);
|
|
553
|
-
|
|
554
|
-
return () => {
|
|
555
|
-
gjsModel.off('change:attributes', handleAttributesChange);
|
|
556
|
-
};
|
|
557
|
-
}, [gjsModel]);
|
|
558
|
-
|
|
559
|
-
useEffect(() => {
|
|
560
|
-
const updateContainerWidth = () => {
|
|
561
|
-
if (containerRef.current) {
|
|
562
|
-
setContainerWidth(containerRef.current.offsetWidth);
|
|
563
|
-
}
|
|
564
|
-
};
|
|
565
|
-
|
|
566
|
-
updateContainerWidth();
|
|
567
|
-
window.addEventListener('resize', updateContainerWidth);
|
|
568
|
-
|
|
569
|
-
return () => window.removeEventListener('resize', updateContainerWidth);
|
|
570
|
-
}, []);
|
|
571
|
-
|
|
572
|
-
const {
|
|
573
|
-
config = {},
|
|
574
|
-
chartType = 'pie',
|
|
575
|
-
chartData = [],
|
|
576
|
-
className,
|
|
577
|
-
title = 'Title',
|
|
578
|
-
subTitle = 'Subtitle',
|
|
579
|
-
} = allData;
|
|
580
|
-
|
|
581
|
-
const defaultClasses = 'aspect-auto h-[250px] w-full';
|
|
582
|
-
const mergedClasses = className ? cn(defaultClasses, className) : defaultClasses;
|
|
583
|
-
|
|
584
|
-
if (chartType === 'pie') {
|
|
585
|
-
const safeChartData = Array.isArray(chartData) ? chartData : [];
|
|
586
|
-
const colors = getNColors(safeChartData.length);
|
|
587
|
-
|
|
588
|
-
for (let i = 0; i < safeChartData.length; i++) {
|
|
589
|
-
const data = safeChartData[i];
|
|
590
|
-
|
|
591
|
-
if (!data.fill) {
|
|
592
|
-
data.fill = colors[i];
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
} else if (!config.color) {
|
|
596
|
-
const safeBaseColors = Array.isArray(baseColors) ? baseColors : [];
|
|
597
|
-
|
|
598
|
-
if (safeBaseColors.length > 0) {
|
|
599
|
-
config.color = safeBaseColors[Math.floor(Math.random() * safeBaseColors.length)];
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
let width: number | undefined = undefined;
|
|
604
|
-
let totalDataPoints = 0;
|
|
605
|
-
|
|
606
|
-
// Helper function to get responsive config
|
|
607
|
-
const getResponsiveConfig = () => {
|
|
608
|
-
if (
|
|
609
|
-
chartType === 'bar' &&
|
|
610
|
-
totalDataPoints > 15 &&
|
|
611
|
-
containerWidth > 0 &&
|
|
612
|
-
width &&
|
|
613
|
-
width <= containerWidth
|
|
614
|
-
) {
|
|
615
|
-
// If chart fits in container, use responsive behavior
|
|
616
|
-
return { ...config, chartUi: {} };
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
return config;
|
|
620
|
-
};
|
|
621
|
-
|
|
622
|
-
if (chartType === 'bar') {
|
|
623
|
-
config.dataKeys = config.dataKeys || [];
|
|
624
|
-
const safeChartData = Array.isArray(chartData) ? chartData : [];
|
|
625
|
-
const safeDataKeys = Array.isArray(config.dataKeys) ? config.dataKeys : [];
|
|
626
|
-
|
|
627
|
-
totalDataPoints = safeChartData.length * Math.max(1, safeDataKeys.length);
|
|
628
|
-
|
|
629
|
-
if (totalDataPoints > 15) {
|
|
630
|
-
width =
|
|
631
|
-
totalDataPoints * 20 +
|
|
632
|
-
safeDataKeys.length * 8 * safeChartData.length +
|
|
633
|
-
safeChartData.length * 15;
|
|
634
|
-
config.chartUi = {
|
|
635
|
-
barCategoryGap: '10%',
|
|
636
|
-
width,
|
|
637
|
-
};
|
|
638
|
-
} else {
|
|
639
|
-
config.chartUi = {};
|
|
640
|
-
}
|
|
641
|
-
} else if (chartType === 'line') {
|
|
642
|
-
config.dataKeys = config.dataKeys || [];
|
|
643
|
-
const safeChartData = Array.isArray(chartData) ? chartData : [];
|
|
644
|
-
totalDataPoints = safeChartData.length;
|
|
645
|
-
|
|
646
|
-
if (totalDataPoints > 15) {
|
|
647
|
-
width = Math.max(800, totalDataPoints * 20);
|
|
648
|
-
config.chartUi = {
|
|
649
|
-
width,
|
|
650
|
-
};
|
|
651
|
-
} else {
|
|
652
|
-
config.chartUi = {};
|
|
653
|
-
}
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
const hasError = attributes.error || allData.error;
|
|
657
|
-
const hasMissing = attributes.isMissing || allData.isMissing;
|
|
658
|
-
const isLoading =
|
|
659
|
-
(attributes.interactionApiInProgress || attributes.loading) && !hasError && !hasMissing;
|
|
660
|
-
|
|
661
|
-
const chartUi = isLoading ? (
|
|
662
|
-
<ChartLoader />
|
|
663
|
-
) : hasError || hasMissing || !chartData ? (
|
|
664
|
-
renderNoDataFallback()
|
|
665
|
-
) : (
|
|
666
|
-
<React.Fragment>
|
|
667
|
-
{(chartType === 'bar' || chartType === 'line') &&
|
|
668
|
-
totalDataPoints > 15 &&
|
|
669
|
-
width &&
|
|
670
|
-
(containerWidth === 0 || width > containerWidth) ? (
|
|
671
|
-
<div className="w-full h-[250px]">
|
|
672
|
-
<ScrollArea className="w-full h-full">
|
|
673
|
-
<div style={{ minWidth: `${width}px`, width: 'max-content', height: '250px' }}>
|
|
674
|
-
<ResponsiveContainer width={width} height={250}>
|
|
675
|
-
<ChartContainer {...rest} config={config} className={mergedClasses}>
|
|
676
|
-
{renderChartComponent(chartType, chartData, config)}
|
|
677
|
-
</ChartContainer>
|
|
678
|
-
</ResponsiveContainer>
|
|
679
|
-
</div>
|
|
680
|
-
<ScrollBar orientation="horizontal" />
|
|
681
|
-
</ScrollArea>
|
|
682
|
-
</div>
|
|
683
|
-
) : (
|
|
684
|
-
<ResponsiveContainer width="100%" height={250}>
|
|
685
|
-
<ChartContainer {...rest} config={getResponsiveConfig()} className={mergedClasses}>
|
|
686
|
-
{renderChartComponent(chartType, chartData, getResponsiveConfig())}
|
|
687
|
-
</ChartContainer>
|
|
688
|
-
</ResponsiveContainer>
|
|
689
|
-
)}
|
|
690
|
-
</React.Fragment>
|
|
691
|
-
);
|
|
692
|
-
|
|
693
|
-
return (
|
|
694
|
-
<Card className="shadow border mt-2 mb-2 relative">
|
|
695
|
-
<CardHeader>
|
|
696
|
-
<div className="flex items-start justify-between">
|
|
697
|
-
<div className="flex-1">
|
|
698
|
-
<CardTitle data-slot="title" contentEditable={isEditable} className="text-2xl">
|
|
699
|
-
{title}
|
|
700
|
-
</CardTitle>
|
|
701
|
-
<CardDescription data-slot="subTitle" contentEditable={isEditable} className="text-sm">
|
|
702
|
-
{subTitle}
|
|
703
|
-
</CardDescription>
|
|
704
|
-
</div>
|
|
705
|
-
</div>
|
|
706
|
-
</CardHeader>
|
|
707
|
-
<CardContent
|
|
708
|
-
ref={containerRef}
|
|
709
|
-
data-slot="bodyContent"
|
|
710
|
-
contentEditable={false}
|
|
711
|
-
className="px-6"
|
|
712
|
-
>
|
|
713
|
-
{chartUi}
|
|
714
|
-
</CardContent>
|
|
715
|
-
<StyledInfoButton
|
|
716
|
-
componentId={gjsModel.get('id')}
|
|
717
|
-
performInteraction={performInteraction}
|
|
718
|
-
position="top-right"
|
|
719
|
-
isVisible={isEditable}
|
|
720
|
-
componentProps={allData}
|
|
721
|
-
/>
|
|
722
|
-
</Card>
|
|
723
|
-
);
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
export function renderTable(props: any): JSX.Element {
|
|
727
|
-
const { isEditable = false, gjsModel, performInteraction = () => {}, ...rest } = props;
|
|
728
|
-
|
|
729
|
-
const [allData, setAllData] = useState({
|
|
730
|
-
...filterPlaceholders(gjsModel.get('componentProps')),
|
|
731
|
-
...filterPlaceholders(props),
|
|
732
|
-
});
|
|
733
|
-
|
|
734
|
-
const cardRef = useRef<HTMLDivElement>(null);
|
|
735
|
-
|
|
736
|
-
const [attributes, setAttributes] = useState({
|
|
737
|
-
...gjsModel.get('attributes'),
|
|
738
|
-
});
|
|
739
|
-
|
|
740
|
-
const {
|
|
741
|
-
data: rawData,
|
|
742
|
-
headerMapper: rawHeaderMapper = {},
|
|
743
|
-
className,
|
|
744
|
-
title = 'Title',
|
|
745
|
-
subTitle = 'Subtitle',
|
|
746
|
-
actions = [],
|
|
747
|
-
pagination,
|
|
748
|
-
} = allData;
|
|
749
|
-
|
|
750
|
-
const data = Array.isArray(rawData) ? rawData : [];
|
|
751
|
-
const headerMapper = (() => {
|
|
752
|
-
const cleaned = { ...rawHeaderMapper };
|
|
753
|
-
|
|
754
|
-
if ('id' in cleaned) {
|
|
755
|
-
delete cleaned.id;
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
if (data.length > 0) {
|
|
759
|
-
const dataKeys = Object.keys(data[0] || {});
|
|
760
|
-
|
|
761
|
-
return Object.fromEntries(Object.entries(cleaned).filter(([key]) => dataKeys.includes(key)));
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
return cleaned;
|
|
765
|
-
})();
|
|
766
|
-
|
|
767
|
-
const sortConfig = (() => {
|
|
768
|
-
const { sortColumn, sortDirection } = gjsModel.get('componentProps') ?? {};
|
|
769
|
-
|
|
770
|
-
return sortColumn && sortDirection ? { [sortColumn]: sortDirection } : {};
|
|
771
|
-
})();
|
|
772
|
-
|
|
773
|
-
const [viewportWidth, setViewportWidth] = useState<number>(0);
|
|
774
|
-
const [scrollAreaHeight, setScrollAreaHeight] = useState<number>(250);
|
|
775
|
-
|
|
776
|
-
const calculateTableHeight = (tableWidth: number, rowCount: number) => {
|
|
777
|
-
if (viewportWidth > 0) {
|
|
778
|
-
const widthRatio = tableWidth / viewportWidth;
|
|
779
|
-
const baseTableHeight = 250;
|
|
780
|
-
let targetScrollAreaHeight = baseTableHeight;
|
|
781
|
-
|
|
782
|
-
if (widthRatio >= 0.8) {
|
|
783
|
-
const headerHeight = 48;
|
|
784
|
-
const rowHeight = 40;
|
|
785
|
-
const calculatedHeight = headerHeight + rowCount * rowHeight;
|
|
786
|
-
targetScrollAreaHeight = Math.min(calculatedHeight, baseTableHeight);
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
const tableId = gjsModel.get('id');
|
|
790
|
-
const editor = gjsModel.em || gjsModel.collection?.em;
|
|
791
|
-
const canvasDocument = editor.Canvas.getDocument() || document;
|
|
792
|
-
const tableElement = canvasDocument.querySelector(
|
|
793
|
-
`[data-table-id="${tableId}"]`,
|
|
794
|
-
) as HTMLElement;
|
|
795
|
-
|
|
796
|
-
if (tableElement) {
|
|
797
|
-
const headerElement = tableElement.querySelector(
|
|
798
|
-
'[data-slot="card-header"]',
|
|
799
|
-
) as HTMLElement;
|
|
800
|
-
|
|
801
|
-
if (headerElement && headerElement.offsetHeight > 0) {
|
|
802
|
-
const actualHeaderHeight = headerElement.offsetHeight;
|
|
803
|
-
const standardHeaderHeight = 58;
|
|
804
|
-
const heightDifference = actualHeaderHeight - standardHeaderHeight;
|
|
805
|
-
|
|
806
|
-
if (heightDifference > 0) {
|
|
807
|
-
targetScrollAreaHeight = targetScrollAreaHeight - heightDifference;
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
setScrollAreaHeight(targetScrollAreaHeight);
|
|
813
|
-
}
|
|
814
|
-
};
|
|
815
|
-
|
|
816
|
-
useEffect(() => {
|
|
817
|
-
const updateViewportWidth = () => {
|
|
818
|
-
const iframe = document.querySelector('.gjs-frame');
|
|
819
|
-
const iframeWindow = (iframe as any)?.contentWindow;
|
|
820
|
-
const iframeDocument = iframeWindow?.document;
|
|
821
|
-
|
|
822
|
-
if (iframeDocument) {
|
|
823
|
-
const dashboardRoot = iframeDocument.getElementById('dashboard-root');
|
|
824
|
-
const effectiveViewportWidth =
|
|
825
|
-
dashboardRoot?.clientWidth || iframeDocument.documentElement.clientWidth;
|
|
826
|
-
setViewportWidth(effectiveViewportWidth);
|
|
827
|
-
}
|
|
828
|
-
};
|
|
829
|
-
|
|
830
|
-
updateViewportWidth();
|
|
831
|
-
window.addEventListener('resize', updateViewportWidth);
|
|
832
|
-
|
|
833
|
-
const iframe = document.querySelector('.gjs-frame');
|
|
834
|
-
const iframeWindow = (iframe as any)?.contentWindow;
|
|
835
|
-
|
|
836
|
-
if (iframeWindow) {
|
|
837
|
-
iframeWindow.addEventListener('resize', updateViewportWidth);
|
|
838
|
-
}
|
|
839
|
-
|
|
840
|
-
return () => {
|
|
841
|
-
window.removeEventListener('resize', updateViewportWidth);
|
|
842
|
-
|
|
843
|
-
if (iframeWindow) {
|
|
844
|
-
iframeWindow.removeEventListener('resize', updateViewportWidth);
|
|
845
|
-
}
|
|
846
|
-
};
|
|
847
|
-
}, []);
|
|
848
|
-
|
|
849
|
-
useEffect(() => {
|
|
850
|
-
const updateTableWidthAndApplyCSS = () => {
|
|
851
|
-
const tableId = gjsModel.get('id');
|
|
852
|
-
const editor = gjsModel.em || gjsModel.collection?.em;
|
|
853
|
-
const canvasDocument = editor.Canvas.getDocument() || document;
|
|
854
|
-
|
|
855
|
-
let tableElement = canvasDocument.querySelector(
|
|
856
|
-
`[data-table-id="${tableId}"]`,
|
|
857
|
-
) as HTMLElement;
|
|
858
|
-
|
|
859
|
-
if (!tableElement && window.parent !== window) {
|
|
860
|
-
tableElement = window.parent.document.querySelector(
|
|
861
|
-
`[data-table-id="${tableId}"]`,
|
|
862
|
-
) as HTMLElement;
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
if (!tableElement) {
|
|
866
|
-
return;
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
const tableActualWidth = tableElement.getBoundingClientRect().width;
|
|
870
|
-
|
|
871
|
-
if (tableActualWidth > 0) {
|
|
872
|
-
calculateTableHeight(tableActualWidth, Array.isArray(data) ? data.length : 0);
|
|
873
|
-
}
|
|
874
|
-
};
|
|
875
|
-
|
|
876
|
-
const timeoutId = setTimeout(updateTableWidthAndApplyCSS, 150);
|
|
877
|
-
window.addEventListener('resize', updateTableWidthAndApplyCSS);
|
|
878
|
-
|
|
879
|
-
return () => {
|
|
880
|
-
clearTimeout(timeoutId);
|
|
881
|
-
window.removeEventListener('resize', updateTableWidthAndApplyCSS);
|
|
882
|
-
};
|
|
883
|
-
}, [allData, gjsModel, viewportWidth, data, calculateTableHeight]);
|
|
884
|
-
|
|
885
|
-
useEffect(() => {
|
|
886
|
-
gjsModel.on('change:componentProps', () => {
|
|
887
|
-
setAllData((prevData: any) => ({
|
|
888
|
-
...prevData, // Preserve existing data (including __sql_query_params)
|
|
889
|
-
...filterPlaceholders(gjsModel.get('componentProps')),
|
|
890
|
-
}));
|
|
891
|
-
});
|
|
892
|
-
}, []);
|
|
893
|
-
|
|
894
|
-
useEffect(() => {
|
|
895
|
-
gjsModel.on('change:attributes', () => {
|
|
896
|
-
setAttributes({
|
|
897
|
-
...gjsModel.get('attributes'),
|
|
898
|
-
});
|
|
899
|
-
});
|
|
900
|
-
}, []);
|
|
901
|
-
|
|
902
|
-
const parsedData = (() => {
|
|
903
|
-
if (
|
|
904
|
-
typeof data === 'object' &&
|
|
905
|
-
data !== null &&
|
|
906
|
-
!Array.isArray(data) &&
|
|
907
|
-
(data as any).name === '__peak_placeholder'
|
|
908
|
-
) {
|
|
909
|
-
return [];
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
if (!Array.isArray(data)) {
|
|
913
|
-
return [];
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
return data.map((datum: any) => {
|
|
917
|
-
const newData = {
|
|
918
|
-
...datum,
|
|
919
|
-
};
|
|
920
|
-
|
|
921
|
-
delete newData.id;
|
|
922
|
-
|
|
923
|
-
return newData;
|
|
924
|
-
});
|
|
925
|
-
})();
|
|
926
|
-
|
|
927
|
-
async function onRowAction(rowIndex: number, actionId: string) {
|
|
928
|
-
const id = gjsModel.get('id');
|
|
929
|
-
|
|
930
|
-
await performInteraction({
|
|
931
|
-
id: actionId,
|
|
932
|
-
interactionType: 'tableButton',
|
|
933
|
-
payload: {
|
|
934
|
-
row: data[rowIndex],
|
|
935
|
-
tableId: id,
|
|
936
|
-
},
|
|
937
|
-
affectedComponents: [],
|
|
938
|
-
});
|
|
939
|
-
}
|
|
940
|
-
|
|
941
|
-
async function onPageChange(pageNumber: number, pageSize: number) {
|
|
942
|
-
const id = gjsModel.get('id');
|
|
943
|
-
const { sortColumn, sortDirection } = gjsModel.get('componentProps') ?? {};
|
|
944
|
-
|
|
945
|
-
await performInteraction({
|
|
946
|
-
id,
|
|
947
|
-
interactionType: 'pagination',
|
|
948
|
-
payload: {
|
|
949
|
-
pageNumber,
|
|
950
|
-
pageSize,
|
|
951
|
-
...(sortColumn && sortDirection && { sortColumn, sortDirection }),
|
|
952
|
-
},
|
|
953
|
-
affectedComponents: getAffectedComponentsWithLoader([id], true),
|
|
954
|
-
});
|
|
955
|
-
}
|
|
956
|
-
|
|
957
|
-
async function handleSort(column: string, direction: SortDirection) {
|
|
958
|
-
const currentProps = gjsModel.get('componentProps');
|
|
959
|
-
const id = gjsModel.get('id');
|
|
960
|
-
|
|
961
|
-
gjsModel.set('componentProps', {
|
|
962
|
-
...currentProps,
|
|
963
|
-
sortColumn: direction ? column : null,
|
|
964
|
-
sortDirection: direction,
|
|
965
|
-
});
|
|
966
|
-
|
|
967
|
-
await performInteraction({
|
|
968
|
-
id,
|
|
969
|
-
interactionType: 'tableSort',
|
|
970
|
-
payload: {
|
|
971
|
-
sortColumn: direction ? column : null,
|
|
972
|
-
sortDirection: direction,
|
|
973
|
-
tableId: id,
|
|
974
|
-
},
|
|
975
|
-
affectedComponents: getAffectedComponentsWithLoader([id], true),
|
|
976
|
-
});
|
|
977
|
-
}
|
|
978
|
-
|
|
979
|
-
return (
|
|
980
|
-
<Card
|
|
981
|
-
ref={cardRef}
|
|
982
|
-
className="shadow border relative flex flex-col h-fit overflow-hidden"
|
|
983
|
-
data-component-type="table"
|
|
984
|
-
data-table-id={gjsModel.get('id')}
|
|
985
|
-
>
|
|
986
|
-
<CardHeader>
|
|
987
|
-
<div className="flex items-start justify-between">
|
|
988
|
-
<div className="flex-1">
|
|
989
|
-
<CardTitle data-slot="title" contentEditable={isEditable} className="text-2xl">
|
|
990
|
-
{title}
|
|
991
|
-
</CardTitle>
|
|
992
|
-
<CardDescription data-slot="subTitle" contentEditable={isEditable} className="text-sm">
|
|
993
|
-
{subTitle}
|
|
994
|
-
</CardDescription>
|
|
995
|
-
</div>
|
|
996
|
-
</div>
|
|
997
|
-
</CardHeader>
|
|
998
|
-
<CardContent
|
|
999
|
-
data-slot="bodyContent"
|
|
1000
|
-
contentEditable={false}
|
|
1001
|
-
className="flex-1 flex flex-col overflow-hidden"
|
|
1002
|
-
>
|
|
1003
|
-
{attributes.error || allData.error || attributes.isMissing || allData.isMissing ? (
|
|
1004
|
-
renderNoDataFallback()
|
|
1005
|
-
) : (
|
|
1006
|
-
<DataTable
|
|
1007
|
-
data={parsedData}
|
|
1008
|
-
headerMapper={headerMapper}
|
|
1009
|
-
className={className}
|
|
1010
|
-
isEditable={isEditable}
|
|
1011
|
-
actions={actions}
|
|
1012
|
-
pagination={pagination}
|
|
1013
|
-
isLoading={attributes.interactionApiInProgress}
|
|
1014
|
-
onRowAction={onRowAction}
|
|
1015
|
-
onPageChange={onPageChange}
|
|
1016
|
-
onSort={handleSort}
|
|
1017
|
-
sortConfig={sortConfig}
|
|
1018
|
-
otherProps={rest}
|
|
1019
|
-
height={scrollAreaHeight}
|
|
1020
|
-
/>
|
|
1021
|
-
)}
|
|
1022
|
-
</CardContent>
|
|
1023
|
-
<StyledInfoButton
|
|
1024
|
-
componentId={gjsModel.get('id')}
|
|
1025
|
-
performInteraction={performInteraction}
|
|
1026
|
-
position="top-right"
|
|
1027
|
-
tableActions={actions}
|
|
1028
|
-
firstRowData={Array.isArray(data) && data.length > 0 ? data[0] : {}}
|
|
1029
|
-
isVisible={isEditable}
|
|
1030
|
-
componentProps={allData}
|
|
1031
|
-
/>
|
|
1032
|
-
</Card>
|
|
1033
|
-
);
|
|
1034
|
-
}
|
|
1035
|
-
|
|
1036
|
-
export function renderTab(props: any): JSX.Element {
|
|
1037
|
-
let tabsWidth = 157;
|
|
1038
|
-
const safeTabs = Array.isArray(props.tabs) ? props.tabs : [];
|
|
1039
|
-
const showFallback = safeTabs.length === 0;
|
|
1040
|
-
|
|
1041
|
-
if (safeTabs.length > 4) {
|
|
1042
|
-
tabsWidth = 157;
|
|
1043
|
-
}
|
|
1044
|
-
|
|
1045
|
-
const defaultValue = props.defaultValue || (safeTabs.length > 0 ? safeTabs[0] : undefined);
|
|
1046
|
-
|
|
1047
|
-
return (
|
|
1048
|
-
<Tabs defaultValue={defaultValue}>
|
|
1049
|
-
<div className="text-center mb-4">
|
|
1050
|
-
<TabsList className="bg-[#F9F9FE] border border-[#D4D5DE] p-1 rounded-lg h-auto">
|
|
1051
|
-
{safeTabs.length > 0 ? (
|
|
1052
|
-
safeTabs.map((tab: string) => (
|
|
1053
|
-
<TabsTrigger
|
|
1054
|
-
value={tab}
|
|
1055
|
-
key={tab}
|
|
1056
|
-
className={`
|
|
1057
|
-
w-[${tabsWidth}px] h-[38px] gap-1 px-3 py-[11px]
|
|
1058
|
-
font-medium text-sm leading-4
|
|
1059
|
-
border-0 bg-[#F9F9FE] text-[#687387] rounded-none
|
|
1060
|
-
data-[state=active]:border data-[state=active]:border-solid data-[state=active]:border-[#2A44D4] data-[state=active]:bg-[#EAECFB] data-[state=active]:text-[#263DBF] data-[state=active]:shadow-none data-[state=active]:rounded-md
|
|
1061
|
-
transition-all duration-200
|
|
1062
|
-
`}
|
|
1063
|
-
style={{
|
|
1064
|
-
fontFamily: 'Helvetica Neue',
|
|
1065
|
-
fontWeight: 500,
|
|
1066
|
-
fontSize: '14px',
|
|
1067
|
-
lineHeight: '16px',
|
|
1068
|
-
letterSpacing: '0%',
|
|
1069
|
-
}}
|
|
1070
|
-
>
|
|
1071
|
-
{tab}
|
|
1072
|
-
</TabsTrigger>
|
|
1073
|
-
))
|
|
1074
|
-
) : (
|
|
1075
|
-
<TabsTrigger
|
|
1076
|
-
value="no-data"
|
|
1077
|
-
className={`
|
|
1078
|
-
w-[${tabsWidth}px] h-[38px] gap-1 px-3 py-[11px]
|
|
1079
|
-
font-medium text-sm leading-4
|
|
1080
|
-
border-0 bg-[#F9F9FE] text-[#687387] rounded-none
|
|
1081
|
-
data-[state=active]:border data-[state=active]:border-solid data-[state=active]:border-[#2A44D4] data-[state=active]:bg-[#EAECFB] data-[state=active]:text-[#263DBF] data-[state=active]:shadow-none data-[state=active]:rounded-md
|
|
1082
|
-
transition-all duration-200
|
|
1083
|
-
`}
|
|
1084
|
-
style={{
|
|
1085
|
-
fontFamily: 'Helvetica Neue',
|
|
1086
|
-
fontWeight: 500,
|
|
1087
|
-
fontSize: '14px',
|
|
1088
|
-
lineHeight: '16px',
|
|
1089
|
-
letterSpacing: '0%',
|
|
1090
|
-
}}
|
|
1091
|
-
disabled
|
|
1092
|
-
>
|
|
1093
|
-
No tabs
|
|
1094
|
-
</TabsTrigger>
|
|
1095
|
-
)}
|
|
1096
|
-
</TabsList>
|
|
1097
|
-
</div>
|
|
1098
|
-
{showFallback ? (
|
|
1099
|
-
<TabsContent
|
|
1100
|
-
value={defaultValue || 'no-data'}
|
|
1101
|
-
className="tabs-content"
|
|
1102
|
-
data-fallback="true"
|
|
1103
|
-
>
|
|
1104
|
-
{renderNoDataFallback()}
|
|
1105
|
-
</TabsContent>
|
|
1106
|
-
) : (
|
|
1107
|
-
safeTabs.map((tab: string) => (
|
|
1108
|
-
<TabsContent value={tab} className="tabs-content" key={tab} />
|
|
1109
|
-
))
|
|
1110
|
-
)}
|
|
1111
|
-
</Tabs>
|
|
1112
|
-
);
|
|
1113
|
-
}
|
|
1114
|
-
|
|
1115
|
-
export function renderMarkdown(props: any): JSX.Element {
|
|
1116
|
-
const { isEditable = false, gjsModel, ...rest } = props;
|
|
1117
|
-
|
|
1118
|
-
const [allData, setAllData] = useState({
|
|
1119
|
-
...filterPlaceholders(gjsModel.get('componentProps')),
|
|
1120
|
-
...filterPlaceholders(props),
|
|
1121
|
-
});
|
|
1122
|
-
|
|
1123
|
-
const [attributes, setAttributes] = useState({
|
|
1124
|
-
...gjsModel.get('attributes'),
|
|
1125
|
-
});
|
|
1126
|
-
|
|
1127
|
-
useEffect(() => {
|
|
1128
|
-
gjsModel.on('change:componentProps', () => {
|
|
1129
|
-
setAllData((prevData: any) => ({
|
|
1130
|
-
...prevData, // Preserve existing data (including __sql_query_params)
|
|
1131
|
-
...filterPlaceholders(gjsModel.get('componentProps')),
|
|
1132
|
-
}));
|
|
1133
|
-
});
|
|
1134
|
-
}, []);
|
|
1135
|
-
|
|
1136
|
-
useEffect(() => {
|
|
1137
|
-
gjsModel.on('change:attributes', () => {
|
|
1138
|
-
setAttributes({
|
|
1139
|
-
...gjsModel.get('attributes'),
|
|
1140
|
-
});
|
|
1141
|
-
});
|
|
1142
|
-
}, []);
|
|
1143
|
-
|
|
1144
|
-
const [isEditing, setIsEditing] = useState(false);
|
|
1145
|
-
const editRef = useRef<HTMLDivElement>(null);
|
|
1146
|
-
|
|
1147
|
-
const startEditing = () => {
|
|
1148
|
-
setIsEditing(true);
|
|
1149
|
-
setTimeout(() => {
|
|
1150
|
-
setupEditor();
|
|
1151
|
-
}, 0);
|
|
1152
|
-
};
|
|
1153
|
-
|
|
1154
|
-
const setupEditor = () => {
|
|
1155
|
-
if (!editRef.current) {
|
|
1156
|
-
return;
|
|
1157
|
-
}
|
|
1158
|
-
|
|
1159
|
-
const editor = editRef.current;
|
|
1160
|
-
editor.innerHTML = '';
|
|
1161
|
-
editor.textContent = allData.summaryText;
|
|
1162
|
-
editor.focus();
|
|
1163
|
-
};
|
|
1164
|
-
|
|
1165
|
-
// Save changes and exit editing mode
|
|
1166
|
-
const saveChanges = () => {
|
|
1167
|
-
if (!editRef.current) {
|
|
1168
|
-
return;
|
|
1169
|
-
}
|
|
1170
|
-
|
|
1171
|
-
const content = editRef.current.innerText || '';
|
|
1172
|
-
setAllData((prev: Record<string, any>) => ({
|
|
1173
|
-
...prev,
|
|
1174
|
-
summaryText: content,
|
|
1175
|
-
}));
|
|
1176
|
-
setIsEditing(false);
|
|
1177
|
-
editRef.current.innerHTML = '';
|
|
1178
|
-
};
|
|
1179
|
-
|
|
1180
|
-
const markdownStyles = {
|
|
1181
|
-
ul: { props: { className: 'list-disc' } },
|
|
1182
|
-
h1: { props: { className: 'text-4xl font-bold leading-[4rem]' } },
|
|
1183
|
-
h2: { props: { className: 'text-3xl font-bold leading-[3rem]' } },
|
|
1184
|
-
h3: { props: { className: 'text-2xl font-bold leading-[2rem]' } },
|
|
1185
|
-
h4: { props: { className: 'text-xl font-bold leading-[2.5rem]' } },
|
|
1186
|
-
h5: { props: { className: 'text-lg font-bold leading-[2.5rem]' } },
|
|
1187
|
-
h6: { props: { className: 'text-base font-bold leading-[2rem]' } },
|
|
1188
|
-
};
|
|
1189
|
-
|
|
1190
|
-
const hasError = attributes.error || allData.error;
|
|
1191
|
-
const hasMissing = attributes.isMissing || allData.isMissing;
|
|
1192
|
-
const isLoading = attributes.loading && !hasError && !hasMissing;
|
|
1193
|
-
|
|
1194
|
-
if (isLoading) {
|
|
1195
|
-
return <MarkdownLoader />;
|
|
1196
|
-
}
|
|
1197
|
-
|
|
1198
|
-
if (hasError || hasMissing) {
|
|
1199
|
-
return (
|
|
1200
|
-
<div className="px-[1em] relative" {...rest}>
|
|
1201
|
-
<h3 className="text-xl font-semibold text-left">Insights</h3>
|
|
1202
|
-
<div
|
|
1203
|
-
className="w-full py-8 flex flex-col items-center justify-center px-6 text-center"
|
|
1204
|
-
style={{ minHeight: '180px' }}
|
|
1205
|
-
>
|
|
1206
|
-
<CircleAlert className="w-12 h-12 text-gray-400 mb-2" />
|
|
1207
|
-
<h3 className="text-lg font-medium text-gray-900">No data available</h3>
|
|
1208
|
-
<p className="mt-2 text-sm text-gray-500 max-w-md">
|
|
1209
|
-
Check your filters or try a different time range.
|
|
1210
|
-
</p>
|
|
1211
|
-
</div>
|
|
1212
|
-
<StyledInfoButton
|
|
1213
|
-
componentId={gjsModel.get('id')}
|
|
1214
|
-
performInteraction={props.performInteraction}
|
|
1215
|
-
position="top-right"
|
|
1216
|
-
isVisible={isEditable}
|
|
1217
|
-
componentProps={allData}
|
|
1218
|
-
/>
|
|
1219
|
-
</div>
|
|
1220
|
-
);
|
|
1221
|
-
}
|
|
1222
|
-
|
|
1223
|
-
if (!allData.isEditable) {
|
|
1224
|
-
return (
|
|
1225
|
-
<div className="px-[1em] relative" {...rest}>
|
|
1226
|
-
<h3 className="text-xl font-semibold text-left">Insights</h3>
|
|
1227
|
-
<div data-slot="summaryText">
|
|
1228
|
-
<Markdown options={{ overrides: markdownStyles }}>{allData.summaryText}</Markdown>
|
|
1229
|
-
</div>
|
|
1230
|
-
<StyledInfoButton
|
|
1231
|
-
componentId={gjsModel.get('id')}
|
|
1232
|
-
performInteraction={props.performInteraction}
|
|
1233
|
-
position="top-right"
|
|
1234
|
-
isVisible={isEditable}
|
|
1235
|
-
componentProps={allData}
|
|
1236
|
-
/>
|
|
1237
|
-
</div>
|
|
1238
|
-
);
|
|
1239
|
-
}
|
|
1240
|
-
|
|
1241
|
-
return (
|
|
1242
|
-
<div className="p-[1em] relative" {...rest}>
|
|
1243
|
-
<h3 className="text-xl font-semibold text-left">Insights</h3>
|
|
1244
|
-
{isEditing ? (
|
|
1245
|
-
<div
|
|
1246
|
-
ref={editRef}
|
|
1247
|
-
contentEditable
|
|
1248
|
-
onBlur={saveChanges}
|
|
1249
|
-
data-slot="summaryText"
|
|
1250
|
-
className="p-2 min-h-[100px] whitespace-pre-wrap"
|
|
1251
|
-
suppressContentEditableWarning={true}
|
|
1252
|
-
style={{
|
|
1253
|
-
whiteSpace: 'pre-wrap',
|
|
1254
|
-
wordWrap: 'break-word',
|
|
1255
|
-
}}
|
|
1256
|
-
/>
|
|
1257
|
-
) : (
|
|
1258
|
-
<div
|
|
1259
|
-
onClick={startEditing}
|
|
1260
|
-
data-slot="summaryText"
|
|
1261
|
-
style={{
|
|
1262
|
-
whiteSpace: 'pre-wrap',
|
|
1263
|
-
wordWrap: 'break-word',
|
|
1264
|
-
}}
|
|
1265
|
-
>
|
|
1266
|
-
<Markdown options={{ overrides: markdownStyles }}>{allData.summaryText}</Markdown>
|
|
1267
|
-
</div>
|
|
1268
|
-
)}
|
|
1269
|
-
<StyledInfoButton
|
|
1270
|
-
componentId={gjsModel.get('id')}
|
|
1271
|
-
performInteraction={props.performInteraction}
|
|
1272
|
-
position="top-right"
|
|
1273
|
-
isVisible={isEditable}
|
|
1274
|
-
componentProps={allData}
|
|
1275
|
-
/>
|
|
1276
|
-
</div>
|
|
1277
|
-
);
|
|
1278
|
-
}
|
|
1279
|
-
|
|
1280
|
-
export function renderButton(props: any): JSX.Element {
|
|
1281
|
-
const { isEditable = false, gjsModel, performInteraction = () => {} } = props;
|
|
1282
|
-
|
|
1283
|
-
const [allData, setAllData] = useState({
|
|
1284
|
-
...props,
|
|
1285
|
-
});
|
|
1286
|
-
|
|
1287
|
-
const [attributes, setAttributes] = useState({
|
|
1288
|
-
...gjsModel.get('attributes'),
|
|
1289
|
-
});
|
|
1290
|
-
|
|
1291
|
-
useEffect(() => {
|
|
1292
|
-
gjsModel.on('change:componentProps', () => {
|
|
1293
|
-
setAllData({
|
|
1294
|
-
...gjsModel.get('componentProps'),
|
|
1295
|
-
});
|
|
1296
|
-
});
|
|
1297
|
-
}, []);
|
|
1298
|
-
|
|
1299
|
-
useEffect(() => {
|
|
1300
|
-
gjsModel.on('change:attributes', () => {
|
|
1301
|
-
setAttributes({
|
|
1302
|
-
...gjsModel.get('attributes'),
|
|
1303
|
-
});
|
|
1304
|
-
});
|
|
1305
|
-
}, []);
|
|
1306
|
-
|
|
1307
|
-
const {
|
|
1308
|
-
text,
|
|
1309
|
-
className,
|
|
1310
|
-
payload,
|
|
1311
|
-
variant = 'default',
|
|
1312
|
-
size = 'sm',
|
|
1313
|
-
affectedComponents,
|
|
1314
|
-
tooltipContent,
|
|
1315
|
-
} = allData;
|
|
1316
|
-
|
|
1317
|
-
function interact() {
|
|
1318
|
-
const id = gjsModel.get('id');
|
|
1319
|
-
performInteraction({
|
|
1320
|
-
id,
|
|
1321
|
-
interactionType: 'button',
|
|
1322
|
-
affectedComponents: getAffectedComponentsWithLoader([id, ...affectedComponents], true),
|
|
1323
|
-
payload,
|
|
1324
|
-
});
|
|
1325
|
-
}
|
|
1326
|
-
|
|
1327
|
-
return (
|
|
1328
|
-
<TooltipButton
|
|
1329
|
-
className={className}
|
|
1330
|
-
isEditable={isEditable}
|
|
1331
|
-
interact={interact}
|
|
1332
|
-
attributes={attributes}
|
|
1333
|
-
variant={variant}
|
|
1334
|
-
size={size}
|
|
1335
|
-
text={text}
|
|
1336
|
-
tooltipContent={tooltipContent}
|
|
1337
|
-
/>
|
|
1338
|
-
);
|
|
1339
|
-
}
|
|
1340
|
-
|
|
1341
|
-
const renderActionCardWithWrapper = (props: any) => (
|
|
1342
|
-
<ComponentWrapper componentProps={{ ...props, componentType: 'ActionCard' }}>
|
|
1343
|
-
{renderActionCard(props)}
|
|
1344
|
-
</ComponentWrapper>
|
|
1345
|
-
);
|
|
1346
|
-
|
|
1347
|
-
const renderButtonWithWrapper = (props: any) => (
|
|
1348
|
-
<ComponentWrapper componentProps={{ ...props, componentType: 'Button' }}>
|
|
1349
|
-
{renderButton(props)}
|
|
1350
|
-
</ComponentWrapper>
|
|
1351
|
-
);
|
|
1352
|
-
|
|
1353
|
-
const renderCardWithWrapper = (props: any) => (
|
|
1354
|
-
<ComponentWrapper componentProps={{ ...props, componentType: 'Card' }}>
|
|
1355
|
-
{renderCard(props)}
|
|
1356
|
-
</ComponentWrapper>
|
|
1357
|
-
);
|
|
1358
|
-
|
|
1359
|
-
const renderChartWithWrapper = (props: any) => (
|
|
1360
|
-
<ComponentWrapper componentProps={{ ...props, componentType: 'Chart' }}>
|
|
1361
|
-
{renderChart(props)}
|
|
1362
|
-
</ComponentWrapper>
|
|
1363
|
-
);
|
|
1364
|
-
|
|
1365
|
-
const renderFilterWithWrapper = (props: any) => (
|
|
1366
|
-
<ComponentWrapper componentProps={{ ...props, componentType: 'Filter' }}>
|
|
1367
|
-
{renderFilter(props)}
|
|
1368
|
-
</ComponentWrapper>
|
|
1369
|
-
);
|
|
1370
|
-
|
|
1371
|
-
const renderMarkdownWithWrapper = (props: any) => (
|
|
1372
|
-
<ComponentWrapper componentProps={{ ...props, componentType: 'Markdown' }}>
|
|
1373
|
-
{renderMarkdown(props)}
|
|
1374
|
-
</ComponentWrapper>
|
|
1375
|
-
);
|
|
1376
|
-
|
|
1377
|
-
const renderSearchWithWrapper = (props: any) => (
|
|
1378
|
-
<ComponentWrapper componentProps={{ ...props, componentType: 'Search' }}>
|
|
1379
|
-
{renderSearch(props)}
|
|
1380
|
-
</ComponentWrapper>
|
|
1381
|
-
);
|
|
1382
|
-
|
|
1383
|
-
const renderTableWithWrapper = (props: any) => (
|
|
1384
|
-
<ComponentWrapper componentProps={{ ...props, componentType: 'Table' }}>
|
|
1385
|
-
{renderTable(props)}
|
|
1386
|
-
</ComponentWrapper>
|
|
1387
|
-
);
|
|
1388
|
-
|
|
1389
|
-
const renderTabWithWrapper = (props: any) => (
|
|
1390
|
-
<ComponentWrapper componentProps={{ ...props, componentType: 'Tabs' }}>
|
|
1391
|
-
{renderTab(props)}
|
|
1392
|
-
</ComponentWrapper>
|
|
1393
|
-
);
|
|
1394
|
-
|
|
1395
|
-
function renderActions(props: any): JSX.Element {
|
|
1396
|
-
const { actions, isEditable = false, gjsModel } = props;
|
|
1397
|
-
|
|
1398
|
-
if (actions === null) {
|
|
1399
|
-
return <div />;
|
|
1400
|
-
}
|
|
1401
|
-
|
|
1402
|
-
if (!Array.isArray(actions) || actions.length === 0) {
|
|
1403
|
-
return (
|
|
1404
|
-
<div className="bg-white text-black p-4 rounded-lg border flex flex-col h-full shadow-none relative">
|
|
1405
|
-
<CardLoader />
|
|
1406
|
-
</div>
|
|
1407
|
-
);
|
|
1408
|
-
}
|
|
1409
|
-
|
|
1410
|
-
return (
|
|
1411
|
-
<div className="dashboard-cards flex flex-wrap gap-4">
|
|
1412
|
-
{actions.map((action: any, index: number) => {
|
|
1413
|
-
const { icon, title, description } = action;
|
|
1414
|
-
|
|
1415
|
-
return (
|
|
1416
|
-
<div key={index} className="flex-1 min-w-[200px]">
|
|
1417
|
-
{renderActionCard({
|
|
1418
|
-
icon,
|
|
1419
|
-
headerContent: title,
|
|
1420
|
-
bodyContent: description,
|
|
1421
|
-
isEditable,
|
|
1422
|
-
gjsModel,
|
|
1423
|
-
})}
|
|
1424
|
-
</div>
|
|
1425
|
-
);
|
|
1426
|
-
})}
|
|
1427
|
-
</div>
|
|
1428
|
-
);
|
|
1429
|
-
}
|
|
1430
|
-
|
|
1431
|
-
const renderActionsWithWrapper = (props: any) => (
|
|
1432
|
-
<ComponentWrapper componentProps={{ ...props, componentType: 'Actions' }}>
|
|
1433
|
-
{renderActions(props)}
|
|
1434
|
-
</ComponentWrapper>
|
|
1435
|
-
);
|
|
1436
|
-
|
|
1437
|
-
export function getRenderers(): Record<string, (props: any) => JSX.Element> {
|
|
1438
|
-
return {
|
|
1439
|
-
ActionCard: renderActionCardWithWrapper,
|
|
1440
|
-
Actions: renderActionsWithWrapper,
|
|
1441
|
-
Button: renderButtonWithWrapper,
|
|
1442
|
-
Card: renderCardWithWrapper,
|
|
1443
|
-
Chart: renderChartWithWrapper,
|
|
1444
|
-
Filter: renderFilterWithWrapper,
|
|
1445
|
-
Markdown: renderMarkdownWithWrapper,
|
|
1446
|
-
Search: renderSearchWithWrapper,
|
|
1447
|
-
Table: renderTableWithWrapper,
|
|
1448
|
-
Tabs: renderTabWithWrapper,
|
|
1449
|
-
};
|
|
1450
|
-
}
|