@juicemantics/veloiq-ui 0.8.5 → 0.9.0
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/dist/index.d.mts +16 -2
- package/dist/index.d.ts +16 -2
- package/dist/index.js +211 -38
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +210 -39
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -200,6 +200,10 @@ interface ModelDef {
|
|
|
200
200
|
description?: string;
|
|
201
201
|
pkField?: string;
|
|
202
202
|
listViewType?: "table" | "gallery" | "calendar" | "totals-details";
|
|
203
|
+
/** Field keys whose values compose this model's record title (space-joined).
|
|
204
|
+
* Configured via `veloiq set-title` and stored on the model's
|
|
205
|
+
* `__veloiq_ui__["titleFields"]`; mirrors the backend `dc_title()`/`__str__`. */
|
|
206
|
+
titleFields?: string[];
|
|
203
207
|
/** True when this ModelDef represents a NamedQuery rather than a plain model table. */
|
|
204
208
|
isNamedQuery?: boolean;
|
|
205
209
|
/** Resource name of the primary model (for show/edit navigation and write routing). */
|
|
@@ -314,6 +318,7 @@ declare const DynamicShow: React__default.FC<{
|
|
|
314
318
|
allModels?: ModelDef[];
|
|
315
319
|
idOverride?: string;
|
|
316
320
|
embedded?: boolean;
|
|
321
|
+
beforeTabs?: React__default.ReactNode;
|
|
317
322
|
}>;
|
|
318
323
|
|
|
319
324
|
interface JourneyCallbacks$1 {
|
|
@@ -489,7 +494,7 @@ declare const LoginPage: React__default.FC<LoginPageProps>;
|
|
|
489
494
|
|
|
490
495
|
declare const DashboardPage: React__default.FC;
|
|
491
496
|
|
|
492
|
-
type CellSourceType = "model" | "named_query" | "field" | "relation" | "custom";
|
|
497
|
+
type CellSourceType = "model" | "named_query" | "field" | "relation" | "custom" | "plotly_chart";
|
|
493
498
|
interface DashboardCell {
|
|
494
499
|
id: string;
|
|
495
500
|
model: string;
|
|
@@ -504,6 +509,8 @@ interface DashboardCell {
|
|
|
504
509
|
max_height: string | null;
|
|
505
510
|
section_name?: string;
|
|
506
511
|
section_id?: string;
|
|
512
|
+
chart_url?: string;
|
|
513
|
+
chart_title?: string;
|
|
507
514
|
}
|
|
508
515
|
interface DashboardTab {
|
|
509
516
|
id: string;
|
|
@@ -618,6 +625,13 @@ declare function generateResources(models: ModelDef[], moduleName: string, optio
|
|
|
618
625
|
*/
|
|
619
626
|
declare const authenticatedFetch: (url: string, options?: RequestInit) => Promise<Response>;
|
|
620
627
|
|
|
628
|
+
declare function useAuthenticatedFileUrl(rawUrl: string): string;
|
|
629
|
+
declare const AuthenticatedImage: React__default.FC<{
|
|
630
|
+
url: string;
|
|
631
|
+
alt?: string;
|
|
632
|
+
style?: React__default.CSSProperties;
|
|
633
|
+
}>;
|
|
634
|
+
|
|
621
635
|
type ModelTone = {
|
|
622
636
|
solid: string;
|
|
623
637
|
soft: string;
|
|
@@ -640,4 +654,4 @@ declare const getModelTone: (modelLike?: string | {
|
|
|
640
654
|
|
|
641
655
|
declare const authSystemModels: ModelDef[];
|
|
642
656
|
|
|
643
|
-
export { API_URL, AllModelsProvider, type BulkActionDef, type CellSourceType, ColorModeContext, ColorModeContextProvider, CommandCenterPortal, type CommandCenterPortalProps, CustomSider, type DashboardCell, type DashboardConfig, DashboardPage, type DashboardTab, DynamicCreate, DynamicEdit, DynamicList, DynamicShow, ExecutableHtml, type FieldDef, GlobalSearch, HierarchyView, HorizontalMenu, InlinePlotlyHtml, LayoutWrapper, type LayoutWrapperProps, LoginPage, type LoginPageProps, type MillerLeafConfig, type ModelDef, ModelHeading, type ModelSearchResult, MultiPaneLayout, type NavConfig, NavConfigContext, type NavConfigEntry, PaneNavigationContext, PinnedRecordsPanel, PrimaryShowContext, type PrimaryShowRendererProps, type RecentActivityData, type RecentActivityGroup, RecentActivityPanel, type RecentRecord, type RecordResult, ReferenceField, type RelationDef, ResourceContext, type ResourceDef, SectionsGrid, ShowFooterButtons, StandardList, StandardShow, type UseRecordSearchReturn, type ViewConfigRow, ViewsGrid, accessControlProvider, authProvider, authSystemModels, authenticatedFetch, buildShowTabFormOptions, generateResources, getModelTone, getNavEntry, guessIcon, httpClient, normalizeToneKey, renderRelationBlock, resolveIcon, setColorSchemas, sortItemsByNavConfig, useAllModels, useKeyboardShortcuts, useMetadataModal, useNavConfig, useNavModules, usePaneNavigation, useRecordSearch, useShowActionsPreferences, useShowEditableForm, useStandardShowTabs };
|
|
657
|
+
export { API_URL, AllModelsProvider, AuthenticatedImage, type BulkActionDef, type CellSourceType, ColorModeContext, ColorModeContextProvider, CommandCenterPortal, type CommandCenterPortalProps, CustomSider, type DashboardCell, type DashboardConfig, DashboardPage, type DashboardTab, DynamicCreate, DynamicEdit, DynamicList, DynamicShow, ExecutableHtml, type FieldDef, GlobalSearch, HierarchyView, HorizontalMenu, InlinePlotlyHtml, LayoutWrapper, type LayoutWrapperProps, LoginPage, type LoginPageProps, type MillerLeafConfig, type ModelDef, ModelHeading, type ModelSearchResult, MultiPaneLayout, type NavConfig, NavConfigContext, type NavConfigEntry, PaneNavigationContext, PinnedRecordsPanel, PrimaryShowContext, type PrimaryShowRendererProps, type RecentActivityData, type RecentActivityGroup, RecentActivityPanel, type RecentRecord, type RecordResult, ReferenceField, type RelationDef, ResourceContext, type ResourceDef, SectionsGrid, ShowFooterButtons, StandardList, StandardShow, type UseRecordSearchReturn, type ViewConfigRow, ViewsGrid, accessControlProvider, authProvider, authSystemModels, authenticatedFetch, buildShowTabFormOptions, generateResources, getModelTone, getNavEntry, guessIcon, httpClient, normalizeToneKey, renderRelationBlock, resolveIcon, setColorSchemas, sortItemsByNavConfig, useAllModels, useAuthenticatedFileUrl, useKeyboardShortcuts, useMetadataModal, useNavConfig, useNavModules, usePaneNavigation, useRecordSearch, useShowActionsPreferences, useShowEditableForm, useStandardShowTabs };
|
package/dist/index.d.ts
CHANGED
|
@@ -200,6 +200,10 @@ interface ModelDef {
|
|
|
200
200
|
description?: string;
|
|
201
201
|
pkField?: string;
|
|
202
202
|
listViewType?: "table" | "gallery" | "calendar" | "totals-details";
|
|
203
|
+
/** Field keys whose values compose this model's record title (space-joined).
|
|
204
|
+
* Configured via `veloiq set-title` and stored on the model's
|
|
205
|
+
* `__veloiq_ui__["titleFields"]`; mirrors the backend `dc_title()`/`__str__`. */
|
|
206
|
+
titleFields?: string[];
|
|
203
207
|
/** True when this ModelDef represents a NamedQuery rather than a plain model table. */
|
|
204
208
|
isNamedQuery?: boolean;
|
|
205
209
|
/** Resource name of the primary model (for show/edit navigation and write routing). */
|
|
@@ -314,6 +318,7 @@ declare const DynamicShow: React__default.FC<{
|
|
|
314
318
|
allModels?: ModelDef[];
|
|
315
319
|
idOverride?: string;
|
|
316
320
|
embedded?: boolean;
|
|
321
|
+
beforeTabs?: React__default.ReactNode;
|
|
317
322
|
}>;
|
|
318
323
|
|
|
319
324
|
interface JourneyCallbacks$1 {
|
|
@@ -489,7 +494,7 @@ declare const LoginPage: React__default.FC<LoginPageProps>;
|
|
|
489
494
|
|
|
490
495
|
declare const DashboardPage: React__default.FC;
|
|
491
496
|
|
|
492
|
-
type CellSourceType = "model" | "named_query" | "field" | "relation" | "custom";
|
|
497
|
+
type CellSourceType = "model" | "named_query" | "field" | "relation" | "custom" | "plotly_chart";
|
|
493
498
|
interface DashboardCell {
|
|
494
499
|
id: string;
|
|
495
500
|
model: string;
|
|
@@ -504,6 +509,8 @@ interface DashboardCell {
|
|
|
504
509
|
max_height: string | null;
|
|
505
510
|
section_name?: string;
|
|
506
511
|
section_id?: string;
|
|
512
|
+
chart_url?: string;
|
|
513
|
+
chart_title?: string;
|
|
507
514
|
}
|
|
508
515
|
interface DashboardTab {
|
|
509
516
|
id: string;
|
|
@@ -618,6 +625,13 @@ declare function generateResources(models: ModelDef[], moduleName: string, optio
|
|
|
618
625
|
*/
|
|
619
626
|
declare const authenticatedFetch: (url: string, options?: RequestInit) => Promise<Response>;
|
|
620
627
|
|
|
628
|
+
declare function useAuthenticatedFileUrl(rawUrl: string): string;
|
|
629
|
+
declare const AuthenticatedImage: React__default.FC<{
|
|
630
|
+
url: string;
|
|
631
|
+
alt?: string;
|
|
632
|
+
style?: React__default.CSSProperties;
|
|
633
|
+
}>;
|
|
634
|
+
|
|
621
635
|
type ModelTone = {
|
|
622
636
|
solid: string;
|
|
623
637
|
soft: string;
|
|
@@ -640,4 +654,4 @@ declare const getModelTone: (modelLike?: string | {
|
|
|
640
654
|
|
|
641
655
|
declare const authSystemModels: ModelDef[];
|
|
642
656
|
|
|
643
|
-
export { API_URL, AllModelsProvider, type BulkActionDef, type CellSourceType, ColorModeContext, ColorModeContextProvider, CommandCenterPortal, type CommandCenterPortalProps, CustomSider, type DashboardCell, type DashboardConfig, DashboardPage, type DashboardTab, DynamicCreate, DynamicEdit, DynamicList, DynamicShow, ExecutableHtml, type FieldDef, GlobalSearch, HierarchyView, HorizontalMenu, InlinePlotlyHtml, LayoutWrapper, type LayoutWrapperProps, LoginPage, type LoginPageProps, type MillerLeafConfig, type ModelDef, ModelHeading, type ModelSearchResult, MultiPaneLayout, type NavConfig, NavConfigContext, type NavConfigEntry, PaneNavigationContext, PinnedRecordsPanel, PrimaryShowContext, type PrimaryShowRendererProps, type RecentActivityData, type RecentActivityGroup, RecentActivityPanel, type RecentRecord, type RecordResult, ReferenceField, type RelationDef, ResourceContext, type ResourceDef, SectionsGrid, ShowFooterButtons, StandardList, StandardShow, type UseRecordSearchReturn, type ViewConfigRow, ViewsGrid, accessControlProvider, authProvider, authSystemModels, authenticatedFetch, buildShowTabFormOptions, generateResources, getModelTone, getNavEntry, guessIcon, httpClient, normalizeToneKey, renderRelationBlock, resolveIcon, setColorSchemas, sortItemsByNavConfig, useAllModels, useKeyboardShortcuts, useMetadataModal, useNavConfig, useNavModules, usePaneNavigation, useRecordSearch, useShowActionsPreferences, useShowEditableForm, useStandardShowTabs };
|
|
657
|
+
export { API_URL, AllModelsProvider, AuthenticatedImage, type BulkActionDef, type CellSourceType, ColorModeContext, ColorModeContextProvider, CommandCenterPortal, type CommandCenterPortalProps, CustomSider, type DashboardCell, type DashboardConfig, DashboardPage, type DashboardTab, DynamicCreate, DynamicEdit, DynamicList, DynamicShow, ExecutableHtml, type FieldDef, GlobalSearch, HierarchyView, HorizontalMenu, InlinePlotlyHtml, LayoutWrapper, type LayoutWrapperProps, LoginPage, type LoginPageProps, type MillerLeafConfig, type ModelDef, ModelHeading, type ModelSearchResult, MultiPaneLayout, type NavConfig, NavConfigContext, type NavConfigEntry, PaneNavigationContext, PinnedRecordsPanel, PrimaryShowContext, type PrimaryShowRendererProps, type RecentActivityData, type RecentActivityGroup, RecentActivityPanel, type RecentRecord, type RecordResult, ReferenceField, type RelationDef, ResourceContext, type ResourceDef, SectionsGrid, ShowFooterButtons, StandardList, StandardShow, type UseRecordSearchReturn, type ViewConfigRow, ViewsGrid, accessControlProvider, authProvider, authSystemModels, authenticatedFetch, buildShowTabFormOptions, generateResources, getModelTone, getNavEntry, guessIcon, httpClient, normalizeToneKey, renderRelationBlock, resolveIcon, setColorSchemas, sortItemsByNavConfig, useAllModels, useAuthenticatedFileUrl, useKeyboardShortcuts, useMetadataModal, useNavConfig, useNavModules, usePaneNavigation, useRecordSearch, useShowActionsPreferences, useShowEditableForm, useStandardShowTabs };
|
package/dist/index.js
CHANGED
|
@@ -5107,6 +5107,39 @@ var getSortPriority = (columnSort, fieldKey) => {
|
|
|
5107
5107
|
const index = columnSort.findIndex((item) => item.fieldKey === fieldKey);
|
|
5108
5108
|
return index === -1 ? 1 : columnSort.length - index + 1;
|
|
5109
5109
|
};
|
|
5110
|
+
var _TOKEN_KEY = "jm_access_token";
|
|
5111
|
+
function useAuthenticatedFileUrl(rawUrl) {
|
|
5112
|
+
const [src, setSrc] = React5.useState("");
|
|
5113
|
+
React5.useEffect(() => {
|
|
5114
|
+
if (!rawUrl) {
|
|
5115
|
+
setSrc("");
|
|
5116
|
+
return;
|
|
5117
|
+
}
|
|
5118
|
+
if (!rawUrl.includes("/api/file/")) {
|
|
5119
|
+
setSrc(rawUrl);
|
|
5120
|
+
return;
|
|
5121
|
+
}
|
|
5122
|
+
const token = localStorage.getItem(_TOKEN_KEY) || "";
|
|
5123
|
+
const controller = new AbortController();
|
|
5124
|
+
let objectUrl = "";
|
|
5125
|
+
fetch(rawUrl, {
|
|
5126
|
+
headers: token ? { Authorization: `Bearer ${token}` } : {},
|
|
5127
|
+
signal: controller.signal
|
|
5128
|
+
}).then((r) => r.ok ? r.blob() : Promise.reject()).then((blob) => {
|
|
5129
|
+
objectUrl = URL.createObjectURL(blob);
|
|
5130
|
+
setSrc(objectUrl);
|
|
5131
|
+
}).catch(() => setSrc(""));
|
|
5132
|
+
return () => {
|
|
5133
|
+
controller.abort();
|
|
5134
|
+
if (objectUrl) URL.revokeObjectURL(objectUrl);
|
|
5135
|
+
};
|
|
5136
|
+
}, [rawUrl]);
|
|
5137
|
+
return src;
|
|
5138
|
+
}
|
|
5139
|
+
var AuthenticatedImage = ({ url, alt, style }) => {
|
|
5140
|
+
const src = useAuthenticatedFileUrl(url);
|
|
5141
|
+
return src ? /* @__PURE__ */ jsxRuntime.jsx("img", { src, alt: alt ?? "", style }) : null;
|
|
5142
|
+
};
|
|
5110
5143
|
var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set(["png", "jpg", "jpeg", "gif", "bmp", "webp", "svg", "tif", "tiff"]);
|
|
5111
5144
|
var isImageRecord = (record) => {
|
|
5112
5145
|
if (record?.avatar_url || record?.image_url || record?.photo_url) return true;
|
|
@@ -5164,7 +5197,7 @@ var renderSharedGalleryCard = ({
|
|
|
5164
5197
|
style: { width: imageWidth, display: "grid", gap: 6, cursor: onClick ? "pointer" : "default" },
|
|
5165
5198
|
onClick,
|
|
5166
5199
|
children: [
|
|
5167
|
-
contentUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
5200
|
+
contentUrl ? /* @__PURE__ */ jsxRuntime.jsx(AuthenticatedImage, { url: contentUrl, alt: label, style: imageStyle }) : /* @__PURE__ */ jsxRuntime.jsx("div", { style: { ...imageStyle, display: "flex", alignItems: "center", justifyContent: "center", color: "#8c8c8c" }, children: /* @__PURE__ */ jsxRuntime.jsx(AntDIcons2.FileTextOutlined, { style: { fontSize: 24 } }) }),
|
|
5168
5201
|
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 12, color: textColor, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }, children: label })
|
|
5169
5202
|
]
|
|
5170
5203
|
},
|
|
@@ -6109,6 +6142,8 @@ var AnalysisChart = ({
|
|
|
6109
6142
|
};
|
|
6110
6143
|
const primarySeriesKey = seriesKeys[0] || "__count__";
|
|
6111
6144
|
const secondarySeriesKey = seriesKeys[1];
|
|
6145
|
+
const resolveNumericField = (fields, n) => fields[Math.min(n, fields.length - 1)] ?? { key: "__count__", label: _10("Count") };
|
|
6146
|
+
const resolveCategoryField = (field1, field2) => field2 ?? field1;
|
|
6112
6147
|
const getNumericValue = (record, key) => {
|
|
6113
6148
|
if (key === "__count__") return 1;
|
|
6114
6149
|
const value = Number(record?.[key]);
|
|
@@ -6525,11 +6560,11 @@ var AnalysisChart = ({
|
|
|
6525
6560
|
] });
|
|
6526
6561
|
};
|
|
6527
6562
|
const renderScatter = (isBubble) => {
|
|
6528
|
-
if (numericFields.length
|
|
6529
|
-
return /* @__PURE__ */ jsxRuntime.jsx(antd.Empty, { description: "Scatter needs at least
|
|
6563
|
+
if (numericFields.length === 0) {
|
|
6564
|
+
return /* @__PURE__ */ jsxRuntime.jsx(antd.Empty, { description: "Scatter needs at least one numeric field." });
|
|
6530
6565
|
}
|
|
6531
|
-
const xField = numericFields
|
|
6532
|
-
const yField = numericFields
|
|
6566
|
+
const xField = resolveNumericField(numericFields, 0);
|
|
6567
|
+
const yField = resolveNumericField(numericFields, 1);
|
|
6533
6568
|
const points = rawRows.map((row) => {
|
|
6534
6569
|
const x = getNumericValue(row, xField.key);
|
|
6535
6570
|
const y = getNumericValue(row, yField.key);
|
|
@@ -6807,11 +6842,12 @@ var AnalysisChart = ({
|
|
|
6807
6842
|
] });
|
|
6808
6843
|
};
|
|
6809
6844
|
const renderHeatmap = () => {
|
|
6810
|
-
if (!categoryField1
|
|
6811
|
-
return /* @__PURE__ */ jsxRuntime.jsx(antd.Empty, { description: "Heatmap needs
|
|
6845
|
+
if (!categoryField1) {
|
|
6846
|
+
return /* @__PURE__ */ jsxRuntime.jsx(antd.Empty, { description: "Heatmap needs a category field." });
|
|
6812
6847
|
}
|
|
6848
|
+
const effectiveCat2 = resolveCategoryField(categoryField1, categoryField2);
|
|
6813
6849
|
const cat1Field = modelField(categoryField1);
|
|
6814
|
-
const cat2Field = modelField(
|
|
6850
|
+
const cat2Field = modelField(effectiveCat2);
|
|
6815
6851
|
const rowLabels = [];
|
|
6816
6852
|
const colLabels = [];
|
|
6817
6853
|
const grid = /* @__PURE__ */ new Map();
|
|
@@ -6889,43 +6925,45 @@ var AnalysisChart = ({
|
|
|
6889
6925
|
] });
|
|
6890
6926
|
};
|
|
6891
6927
|
const renderCrosstab = () => {
|
|
6892
|
-
if (!categoryField1
|
|
6893
|
-
return /* @__PURE__ */ jsxRuntime.jsx(antd.Empty, { description: "Crosstab needs
|
|
6928
|
+
if (!categoryField1) {
|
|
6929
|
+
return /* @__PURE__ */ jsxRuntime.jsx(antd.Empty, { description: "Crosstab needs a category field." });
|
|
6894
6930
|
}
|
|
6931
|
+
const effectiveCat2 = resolveCategoryField(categoryField1, categoryField2);
|
|
6895
6932
|
const cat1Field = modelField(categoryField1);
|
|
6896
|
-
const cat2Field = modelField(
|
|
6933
|
+
const cat2Field = modelField(effectiveCat2);
|
|
6897
6934
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
6898
6935
|
CrosstabTable,
|
|
6899
6936
|
{
|
|
6900
6937
|
rows: rawRows,
|
|
6901
6938
|
rowField: categoryField1,
|
|
6902
|
-
colField:
|
|
6939
|
+
colField: effectiveCat2 ?? categoryField1,
|
|
6903
6940
|
cellFieldKeys: seriesKeys,
|
|
6904
6941
|
cellFieldLabels: seriesLabels,
|
|
6905
6942
|
allFields,
|
|
6906
6943
|
summaryFn,
|
|
6907
6944
|
formatCategoryValue,
|
|
6908
6945
|
numericBarColor,
|
|
6909
|
-
caption: `${_10("Crosstab")}: ${cat1Field?.label || categoryField1} \xD7 ${cat2Field?.label ||
|
|
6946
|
+
caption: `${_10("Crosstab")}: ${cat1Field?.label || categoryField1} \xD7 ${cat2Field?.label || effectiveCat2} (${summaryFn})`
|
|
6910
6947
|
}
|
|
6911
6948
|
);
|
|
6912
6949
|
};
|
|
6913
6950
|
const renderRadar = () => {
|
|
6914
|
-
if (seriesKeys.length
|
|
6915
|
-
return /* @__PURE__ */ jsxRuntime.jsx(antd.Empty, { description: "Radar needs at least
|
|
6951
|
+
if (seriesKeys.length === 0) {
|
|
6952
|
+
return /* @__PURE__ */ jsxRuntime.jsx(antd.Empty, { description: "Radar needs at least one series." });
|
|
6916
6953
|
}
|
|
6954
|
+
const effectiveSeriesKeys = seriesKeys.length >= 3 ? seriesKeys : Array.from({ length: 3 }, (_43, i) => seriesKeys[i % seriesKeys.length]);
|
|
6917
6955
|
const centerX = paddingLeft + chartWidth / 2;
|
|
6918
6956
|
const centerY = paddingTop + chartHeight / 2;
|
|
6919
6957
|
const radius = Math.min(chartWidth, chartHeight) * 0.35;
|
|
6920
|
-
const maxBySeries =
|
|
6958
|
+
const maxBySeries = effectiveSeriesKeys.reduce((acc, key) => {
|
|
6921
6959
|
acc[key] = Math.max(...data.map((group) => group.values[key] || 0), 1);
|
|
6922
6960
|
return acc;
|
|
6923
6961
|
}, {});
|
|
6924
|
-
const angleStep = Math.PI * 2 /
|
|
6962
|
+
const angleStep = Math.PI * 2 / effectiveSeriesKeys.length;
|
|
6925
6963
|
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { ref: svgRef, className: "chart-plot", viewBox: `0 0 ${width} ${height}`, width: "100%", height, role: "img", children: [
|
|
6926
6964
|
renderTitle(),
|
|
6927
6965
|
renderCaption("Radar chart"),
|
|
6928
|
-
|
|
6966
|
+
effectiveSeriesKeys.map((seriesKey, index) => {
|
|
6929
6967
|
const angle = -Math.PI / 2 + index * angleStep;
|
|
6930
6968
|
const x = centerX + radius * Math.cos(angle);
|
|
6931
6969
|
const y = centerY + radius * Math.sin(angle);
|
|
@@ -6935,7 +6973,7 @@ var AnalysisChart = ({
|
|
|
6935
6973
|
] }, `radar-axis-${seriesKey}`);
|
|
6936
6974
|
}),
|
|
6937
6975
|
data.map((group, groupIndex) => {
|
|
6938
|
-
const points =
|
|
6976
|
+
const points = effectiveSeriesKeys.map((seriesKey, index) => {
|
|
6939
6977
|
const value = group.values[seriesKey] || 0;
|
|
6940
6978
|
const ratio = value / Math.max(1, maxBySeries[seriesKey]);
|
|
6941
6979
|
const angle = -Math.PI / 2 + index * angleStep;
|
|
@@ -6960,13 +6998,74 @@ var AnalysisChart = ({
|
|
|
6960
6998
|
})
|
|
6961
6999
|
] });
|
|
6962
7000
|
};
|
|
6963
|
-
const
|
|
6964
|
-
if (
|
|
6965
|
-
return /* @__PURE__ */ jsxRuntime.jsx(antd.Empty, { description: "
|
|
7001
|
+
const render3D = () => {
|
|
7002
|
+
if (numericFields.length === 0) {
|
|
7003
|
+
return /* @__PURE__ */ jsxRuntime.jsx(antd.Empty, { description: "3D scatter needs at least one numeric field." });
|
|
6966
7004
|
}
|
|
7005
|
+
const xField = resolveNumericField(numericFields, 0);
|
|
7006
|
+
const yField = resolveNumericField(numericFields, 1);
|
|
7007
|
+
const zField = resolveNumericField(numericFields, 2);
|
|
7008
|
+
const points = rawRows.map((row) => {
|
|
7009
|
+
const x = getNumericValue(row, xField.key);
|
|
7010
|
+
const y = getNumericValue(row, yField.key);
|
|
7011
|
+
const z = getNumericValue(row, zField.key);
|
|
7012
|
+
if (x === null || y === null || z === null) return null;
|
|
7013
|
+
return { x, y, z };
|
|
7014
|
+
}).filter((p) => !!p);
|
|
7015
|
+
if (points.length === 0) return renderNoChartDataMessage();
|
|
7016
|
+
const xs = points.map((p) => p.x);
|
|
7017
|
+
const ys = points.map((p) => p.y);
|
|
7018
|
+
const zs = points.map((p) => p.z);
|
|
7019
|
+
const xMin = Math.min(...xs), xMax = Math.max(...xs);
|
|
7020
|
+
const yMin = Math.min(...ys), yMax = Math.max(...ys);
|
|
7021
|
+
const zMin = Math.min(...zs), zMax = Math.max(...zs);
|
|
7022
|
+
const norm = (v, lo, hi) => hi === lo ? 0.5 : (v - lo) / (hi - lo);
|
|
7023
|
+
const isoScale = Math.min(chartWidth, chartHeight) * 0.38;
|
|
7024
|
+
const cx = paddingLeft + chartWidth * 0.5;
|
|
7025
|
+
const cy = paddingTop + chartHeight * 0.55;
|
|
7026
|
+
const cos30 = Math.cos(Math.PI / 6);
|
|
7027
|
+
const sin30 = Math.sin(Math.PI / 6);
|
|
7028
|
+
const project = (nx, ny, nz) => ({
|
|
7029
|
+
sx: cx + (nx - nz) * cos30 * isoScale,
|
|
7030
|
+
sy: cy - ny * isoScale + (nx + nz) * sin30 * isoScale
|
|
7031
|
+
});
|
|
7032
|
+
const axisEnd = (nx, ny, nz) => project(nx, ny, nz);
|
|
7033
|
+
const origin = project(0, 0, 0);
|
|
7034
|
+
const xTip = axisEnd(1, 0, 0);
|
|
7035
|
+
const yTip = axisEnd(0, 1, 0);
|
|
7036
|
+
const zTip = axisEnd(0, 0, 1);
|
|
7037
|
+
const projected = points.map(
|
|
7038
|
+
(p) => project(norm(p.x, xMin, xMax), norm(p.y, yMin, yMax), norm(p.z, zMin, zMax))
|
|
7039
|
+
);
|
|
7040
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { ref: svgRef, className: "chart-plot", viewBox: `0 0 ${width} ${height}`, width: "100%", height, role: "img", children: [
|
|
7041
|
+
renderTitle(),
|
|
7042
|
+
renderCaption(`3D: ${xField.label} \xD7 ${yField.label} \xD7 ${zField.label}`),
|
|
7043
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: origin.sx, y1: origin.sy, x2: xTip.sx, y2: xTip.sy, stroke: colors[0], strokeWidth: 1.5, opacity: 0.6 }),
|
|
7044
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: origin.sx, y1: origin.sy, x2: yTip.sx, y2: yTip.sy, stroke: colors[1], strokeWidth: 1.5, opacity: 0.6 }),
|
|
7045
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: origin.sx, y1: origin.sy, x2: zTip.sx, y2: zTip.sy, stroke: colors[2], strokeWidth: 1.5, opacity: 0.6 }),
|
|
7046
|
+
/* @__PURE__ */ jsxRuntime.jsx("text", { x: xTip.sx + 4, y: xTip.sy + 4, fontSize: "11", fill: colors[0], children: xField.label }),
|
|
7047
|
+
/* @__PURE__ */ jsxRuntime.jsx("text", { x: yTip.sx + 4, y: yTip.sy, fontSize: "11", fill: colors[1], children: yField.label }),
|
|
7048
|
+
/* @__PURE__ */ jsxRuntime.jsx("text", { x: zTip.sx + 4, y: zTip.sy + 4, fontSize: "11", fill: colors[2], children: zField.label }),
|
|
7049
|
+
projected.map((p, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
7050
|
+
"circle",
|
|
7051
|
+
{
|
|
7052
|
+
className: "chart-item chart-point",
|
|
7053
|
+
style: { "--delay": `${i * 8}ms` },
|
|
7054
|
+
cx: p.sx,
|
|
7055
|
+
cy: p.sy,
|
|
7056
|
+
r: 4,
|
|
7057
|
+
fill: colors[0],
|
|
7058
|
+
opacity: 0.7
|
|
7059
|
+
},
|
|
7060
|
+
`3d-${i}`
|
|
7061
|
+
))
|
|
7062
|
+
] });
|
|
7063
|
+
};
|
|
7064
|
+
const renderCombo = () => {
|
|
7065
|
+
const effectiveSecondaryKey = secondarySeriesKey ?? primarySeriesKey;
|
|
6967
7066
|
const valuesCombo = data.flatMap((group) => [
|
|
6968
7067
|
group.values[primarySeriesKey] || 0,
|
|
6969
|
-
group.values[
|
|
7068
|
+
group.values[effectiveSecondaryKey] || 0
|
|
6970
7069
|
]);
|
|
6971
7070
|
const maxCombo = Math.max(...valuesCombo, 1);
|
|
6972
7071
|
const minCombo = Math.min(...valuesCombo, 0);
|
|
@@ -6975,7 +7074,7 @@ var AnalysisChart = ({
|
|
|
6975
7074
|
const barWidth2 = groupWidth2 * 0.6;
|
|
6976
7075
|
const points = data.map((group, index) => {
|
|
6977
7076
|
const x = paddingLeft + index * groupWidth2 + groupWidth2 / 2;
|
|
6978
|
-
const y = scaleYCombo(group.values[
|
|
7077
|
+
const y = scaleYCombo(group.values[effectiveSecondaryKey] || 0);
|
|
6979
7078
|
return `${x},${y}`;
|
|
6980
7079
|
}).join(" ");
|
|
6981
7080
|
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { ref: svgRef, className: "chart-plot", viewBox: `0 0 ${width} ${height}`, width: "100%", height, role: "img", children: [
|
|
@@ -6983,7 +7082,7 @@ var AnalysisChart = ({
|
|
|
6983
7082
|
renderLegendItems(
|
|
6984
7083
|
[
|
|
6985
7084
|
{ label: seriesLabels[primarySeriesKey] || primarySeriesKey, color: colors[0] },
|
|
6986
|
-
{ label: seriesLabels[
|
|
7085
|
+
{ label: seriesLabels[effectiveSecondaryKey] || effectiveSecondaryKey, color: colors[2] }
|
|
6987
7086
|
],
|
|
6988
7087
|
8
|
|
6989
7088
|
),
|
|
@@ -7034,13 +7133,14 @@ var AnalysisChart = ({
|
|
|
7034
7133
|
chartType === "histogram" && renderHistogram(),
|
|
7035
7134
|
chartType === "scatter" && renderScatter(false),
|
|
7036
7135
|
chartType === "bubble" && renderScatter(true),
|
|
7037
|
-
chartType === "box" && renderBoxPlot(),
|
|
7136
|
+
(chartType === "box" || chartType === "boxplot") && renderBoxPlot(),
|
|
7038
7137
|
chartType === "waterfall" && renderWaterfall(),
|
|
7039
7138
|
chartType === "heatmap" && renderHeatmap(),
|
|
7040
7139
|
chartType === "crosstab" && renderCrosstab(),
|
|
7041
7140
|
chartType === "radar" && renderRadar(),
|
|
7042
7141
|
chartType === "combo" && renderCombo(),
|
|
7043
|
-
chartType
|
|
7142
|
+
chartType === "3d" && render3D(),
|
|
7143
|
+
(chartType === "bar" || chartType === "line" || chartType === "area" || chartType === "stacked" || chartType === "bar-horizontal" || chartType === "stacked-horizontal" || chartType === "area-horizontal") && /* @__PURE__ */ jsxRuntime.jsxs("svg", { ref: svgRef, className: "chart-plot", viewBox: `0 0 ${width} ${height}`, width: "100%", height, role: "img", children: [
|
|
7044
7144
|
renderTitle(),
|
|
7045
7145
|
renderLegendItems(
|
|
7046
7146
|
seriesKeys.map((seriesKey, index) => ({
|
|
@@ -7487,6 +7587,13 @@ body, table, th, td, input, button, select, textarea, div, span, p, li, ul, ol {
|
|
|
7487
7587
|
if (syncHeightTimerRef.current) clearTimeout(syncHeightTimerRef.current);
|
|
7488
7588
|
};
|
|
7489
7589
|
}, []);
|
|
7590
|
+
const inlineHtml = React5.useMemo(
|
|
7591
|
+
() => (html || "").replace(
|
|
7592
|
+
/<script\b[^>]*\bsrc=["']?[^"'>]*cdn\.plot\.ly[^"'>]*["']?[^>]*><\/script>/gi,
|
|
7593
|
+
""
|
|
7594
|
+
),
|
|
7595
|
+
[html]
|
|
7596
|
+
);
|
|
7490
7597
|
if (mode === "iframe") {
|
|
7491
7598
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
7492
7599
|
"iframe",
|
|
@@ -7497,7 +7604,7 @@ body, table, th, td, input, button, select, textarea, div, span, p, li, ul, ol {
|
|
|
7497
7604
|
}
|
|
7498
7605
|
);
|
|
7499
7606
|
}
|
|
7500
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: htmlRef, dangerouslySetInnerHTML: { __html:
|
|
7607
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: htmlRef, dangerouslySetInnerHTML: { __html: inlineHtml }, style });
|
|
7501
7608
|
};
|
|
7502
7609
|
|
|
7503
7610
|
// src/components/DynamicResource/relations/helpers.ts
|
|
@@ -8713,7 +8820,7 @@ function useRoleFilteredModel(model) {
|
|
|
8713
8820
|
}, [model, userRoles]);
|
|
8714
8821
|
}
|
|
8715
8822
|
var _19 = window._ || ((text) => text);
|
|
8716
|
-
var DynamicShow = ({ model: modelProp, allModels, idOverride, embedded }) => {
|
|
8823
|
+
var DynamicShow = ({ model: modelProp, allModels, idOverride, embedded, beforeTabs }) => {
|
|
8717
8824
|
const model = useRoleFilteredModel(modelProp);
|
|
8718
8825
|
applyI18nLabelsToModel(model);
|
|
8719
8826
|
applyI18nLabelsToModels(allModels);
|
|
@@ -8775,6 +8882,7 @@ var DynamicShow = ({ model: modelProp, allModels, idOverride, embedded }) => {
|
|
|
8775
8882
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "jm-tone-scope", style: toneScopeStyle(modelTone), children: [
|
|
8776
8883
|
/* @__PURE__ */ jsxRuntime.jsx(ToneSharedStyles, {}),
|
|
8777
8884
|
!record ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", justifyContent: "center", padding: 32 }, children: /* @__PURE__ */ jsxRuntime.jsx(antd.Spin, {}) }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
8885
|
+
beforeTabs,
|
|
8778
8886
|
/* @__PURE__ */ jsxRuntime.jsx(antd.Tabs, { activeKey: activeTabKey, onChange: setActiveTabKey, items: lazyItems, destroyInactiveTabPane: true }),
|
|
8779
8887
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8780
8888
|
ShowFooterButtons,
|
|
@@ -8802,6 +8910,7 @@ var DynamicShow = ({ model: modelProp, allModels, idOverride, embedded }) => {
|
|
|
8802
8910
|
})),
|
|
8803
8911
|
headerButtons,
|
|
8804
8912
|
children: [
|
|
8913
|
+
beforeTabs,
|
|
8805
8914
|
/* @__PURE__ */ jsxRuntime.jsx(antd.Tabs, { activeKey: activeTabKey, onChange: setActiveTabKey, items: lazyItems, destroyInactiveTabPane: true }),
|
|
8806
8915
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8807
8916
|
ShowFooterButtons,
|
|
@@ -9779,7 +9888,9 @@ var CellConfigDrawer = ({ open, cell, tabId, config, onClose, onSave }) => {
|
|
|
9779
9888
|
min_width: cell.min_width ?? "",
|
|
9780
9889
|
max_width: cell.max_width ?? "",
|
|
9781
9890
|
min_height: cell.min_height ?? "",
|
|
9782
|
-
max_height: cell.max_height ?? ""
|
|
9891
|
+
max_height: cell.max_height ?? "",
|
|
9892
|
+
chart_url: cell.chart_url ?? "",
|
|
9893
|
+
chart_title: cell.chart_title ?? ""
|
|
9783
9894
|
});
|
|
9784
9895
|
}, [cell, tabId, config, form]);
|
|
9785
9896
|
const handleSave = () => {
|
|
@@ -9795,7 +9906,9 @@ var CellConfigDrawer = ({ open, cell, tabId, config, onClose, onSave }) => {
|
|
|
9795
9906
|
min_width: values.min_width || null,
|
|
9796
9907
|
max_width: values.max_width || null,
|
|
9797
9908
|
min_height: values.min_height || null,
|
|
9798
|
-
max_height: values.max_height || null
|
|
9909
|
+
max_height: values.max_height || null,
|
|
9910
|
+
chart_url: values.chart_url || void 0,
|
|
9911
|
+
chart_title: values.chart_title || void 0
|
|
9799
9912
|
};
|
|
9800
9913
|
const currentTab = config.tabs.find((t) => t.id === tabId);
|
|
9801
9914
|
const nameUnchanged = currentTab?.name.trim().toLowerCase() === newTabName.toLowerCase();
|
|
@@ -9842,7 +9955,7 @@ var CellConfigDrawer = ({ open, cell, tabId, config, onClose, onSave }) => {
|
|
|
9842
9955
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
9843
9956
|
antd.Drawer,
|
|
9844
9957
|
{
|
|
9845
|
-
title: cell?.source_type !== "model" ? `Configure section: ${cell?.section_name ?? cell?.model ?? ""}` : `Configure cell: ${cell?.model ?? ""}`,
|
|
9958
|
+
title: cell?.source_type === "plotly_chart" ? `Configure chart: ${cell?.chart_title ?? cell?.model ?? ""}` : cell?.source_type !== "model" ? `Configure section: ${cell?.section_name ?? cell?.model ?? ""}` : `Configure cell: ${cell?.model ?? ""}`,
|
|
9846
9959
|
placement: "right",
|
|
9847
9960
|
width: 380,
|
|
9848
9961
|
open,
|
|
@@ -9870,6 +9983,11 @@ var CellConfigDrawer = ({ open, cell, tabId, config, onClose, onSave }) => {
|
|
|
9870
9983
|
/* @__PURE__ */ jsxRuntime.jsx(antd.Divider, { orientation: "left", children: "View" }),
|
|
9871
9984
|
/* @__PURE__ */ jsxRuntime.jsx(antd.Form.Item, { name: "view_type", label: "View type", children: /* @__PURE__ */ jsxRuntime.jsx(antd.Select, { options: VIEW_TYPE_OPTIONS }) })
|
|
9872
9985
|
] }),
|
|
9986
|
+
cell?.source_type === "plotly_chart" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
9987
|
+
/* @__PURE__ */ jsxRuntime.jsx(antd.Divider, { orientation: "left", children: "Chart" }),
|
|
9988
|
+
/* @__PURE__ */ jsxRuntime.jsx(antd.Form.Item, { name: "chart_title", label: "Chart title", children: /* @__PURE__ */ jsxRuntime.jsx(antd.Input, { placeholder: "e.g. Confidence by Month" }) }),
|
|
9989
|
+
/* @__PURE__ */ jsxRuntime.jsx(antd.Form.Item, { name: "chart_url", label: "Chart URL", children: /* @__PURE__ */ jsxRuntime.jsx(antd.Input, { placeholder: "/api/nl-answers-confidence-by-month-chart" }) })
|
|
9990
|
+
] }),
|
|
9873
9991
|
/* @__PURE__ */ jsxRuntime.jsx(antd.Divider, { orientation: "left", children: "Size" }),
|
|
9874
9992
|
/* @__PURE__ */ jsxRuntime.jsxs(antd.Space, { wrap: true, children: [
|
|
9875
9993
|
/* @__PURE__ */ jsxRuntime.jsx(antd.Form.Item, { name: "min_width", label: "Min width", style: { marginBottom: 0 }, children: /* @__PURE__ */ jsxRuntime.jsx(antd.Input, { placeholder: "e.g. 320px", style: { width: 130 } }) }),
|
|
@@ -10074,6 +10192,13 @@ var SectionsGrid = ({ cells, config, tabId, renderContent, onConfigChange, isCon
|
|
|
10074
10192
|
if (!cells.length) return 1;
|
|
10075
10193
|
return Math.max(...cells.map((c) => c.row)) + 1;
|
|
10076
10194
|
}, [cells]);
|
|
10195
|
+
const soloRows = React5.useMemo(() => {
|
|
10196
|
+
const counts = /* @__PURE__ */ new Map();
|
|
10197
|
+
for (const c of cells) counts.set(c.row, (counts.get(c.row) ?? 0) + 1);
|
|
10198
|
+
const solo = /* @__PURE__ */ new Set();
|
|
10199
|
+
for (const [row, count] of counts) if (count === 1) solo.add(row);
|
|
10200
|
+
return solo;
|
|
10201
|
+
}, [cells]);
|
|
10077
10202
|
const visibleCells = maximizedCellId ? cells.filter((c) => c.id === maximizedCellId) : cells;
|
|
10078
10203
|
const gridStyle = {
|
|
10079
10204
|
display: "grid",
|
|
@@ -10092,7 +10217,7 @@ var SectionsGrid = ({ cells, config, tabId, renderContent, onConfigChange, isCon
|
|
|
10092
10217
|
"div",
|
|
10093
10218
|
{
|
|
10094
10219
|
style: {
|
|
10095
|
-
gridColumn: maximizedCellId ? "1 / -1" : `${cell.col + 1}`,
|
|
10220
|
+
gridColumn: maximizedCellId || soloRows.has(cell.row) ? "1 / -1" : `${cell.col + 1}`,
|
|
10096
10221
|
gridRow: maximizedCellId ? "1 / -1" : `${cell.row + 1}`
|
|
10097
10222
|
},
|
|
10098
10223
|
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -15413,7 +15538,8 @@ var RelatedObjectsTable = ({ rel, record, relatedModel, parentModel, showActions
|
|
|
15413
15538
|
{ label: _34("Heatmap"), value: "heatmap" },
|
|
15414
15539
|
{ label: _34("Crosstab"), value: "crosstab" },
|
|
15415
15540
|
{ label: _34("Radar"), value: "radar" },
|
|
15416
|
-
{ label: _34("Combo (Bar + Line)"), value: "combo" }
|
|
15541
|
+
{ label: _34("Combo (Bar + Line)"), value: "combo" },
|
|
15542
|
+
{ label: _34("3D Scatter"), value: "3d" }
|
|
15417
15543
|
]
|
|
15418
15544
|
}
|
|
15419
15545
|
)
|
|
@@ -16688,7 +16814,7 @@ var DynamicList = ({ model: modelProp, allModels, filter, relationConfig, isEmbe
|
|
|
16688
16814
|
if (["true", "1", "t", "yes", "y"].includes(normalized)) value = true;
|
|
16689
16815
|
if (["false", "0", "f", "no", "n"].includes(normalized)) value = false;
|
|
16690
16816
|
}
|
|
16691
|
-
return [{ field: searchField.key, operator: "
|
|
16817
|
+
return [{ field: searchField.key, operator: "contains", value }];
|
|
16692
16818
|
}
|
|
16693
16819
|
});
|
|
16694
16820
|
const [allRowsData, setAllRowsData] = React5.useState([]);
|
|
@@ -19944,7 +20070,8 @@ var DynamicList = ({ model: modelProp, allModels, filter, relationConfig, isEmbe
|
|
|
19944
20070
|
{ label: _36("Heatmap"), value: "heatmap" },
|
|
19945
20071
|
{ label: _36("Crosstab"), value: "crosstab" },
|
|
19946
20072
|
{ label: _36("Radar"), value: "radar" },
|
|
19947
|
-
{ label: _36("Combo (Bar + Line)"), value: "combo" }
|
|
20073
|
+
{ label: _36("Combo (Bar + Line)"), value: "combo" },
|
|
20074
|
+
{ label: _36("3D Scatter"), value: "3d" }
|
|
19948
20075
|
]
|
|
19949
20076
|
}
|
|
19950
20077
|
)
|
|
@@ -21104,6 +21231,48 @@ function useDashboardConfig() {
|
|
|
21104
21231
|
}, [apiUrl]);
|
|
21105
21232
|
return { config, enabled, loading, save, reload: load };
|
|
21106
21233
|
}
|
|
21234
|
+
var PlotlyChartContent = ({ chartUrl, refreshNonce }) => {
|
|
21235
|
+
const [chartHtml, setChartHtml] = React5.useState("");
|
|
21236
|
+
const [loading, setLoading] = React5.useState(true);
|
|
21237
|
+
const [error, setError] = React5.useState("");
|
|
21238
|
+
const fetchChart = React5.useCallback(async () => {
|
|
21239
|
+
setLoading(true);
|
|
21240
|
+
setError("");
|
|
21241
|
+
try {
|
|
21242
|
+
const apiUrl = typeof API_URL3 === "string" ? API_URL3 : "";
|
|
21243
|
+
const fullUrl = chartUrl.startsWith("http") ? chartUrl : `${apiUrl}${chartUrl}`;
|
|
21244
|
+
const sep = fullUrl.includes("?") ? "&" : "?";
|
|
21245
|
+
const lang = (() => {
|
|
21246
|
+
try {
|
|
21247
|
+
return (localStorage.getItem("locale") || navigator.language || "en").split("-")[0].toLowerCase();
|
|
21248
|
+
} catch {
|
|
21249
|
+
return "en";
|
|
21250
|
+
}
|
|
21251
|
+
})();
|
|
21252
|
+
const res = await authenticatedFetch(`${fullUrl}${sep}lang=${encodeURIComponent(lang)}`);
|
|
21253
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
21254
|
+
const data = await res.json();
|
|
21255
|
+
setChartHtml(data.chart_html || "");
|
|
21256
|
+
} catch (e) {
|
|
21257
|
+
setError(e?.message ?? String(e));
|
|
21258
|
+
} finally {
|
|
21259
|
+
setLoading(false);
|
|
21260
|
+
}
|
|
21261
|
+
}, [chartUrl]);
|
|
21262
|
+
React5.useEffect(() => {
|
|
21263
|
+
fetchChart();
|
|
21264
|
+
}, [fetchChart, refreshNonce]);
|
|
21265
|
+
if (loading) {
|
|
21266
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", justifyContent: "center", alignItems: "center", height: "100%", minHeight: 200 }, children: /* @__PURE__ */ jsxRuntime.jsx(antd.Spin, {}) });
|
|
21267
|
+
}
|
|
21268
|
+
if (error) {
|
|
21269
|
+
return /* @__PURE__ */ jsxRuntime.jsx(antd.Empty, { description: `Chart error: ${error}`, style: { padding: 20 }, image: antd.Empty.PRESENTED_IMAGE_SIMPLE });
|
|
21270
|
+
}
|
|
21271
|
+
if (!chartHtml) {
|
|
21272
|
+
return /* @__PURE__ */ jsxRuntime.jsx(antd.Empty, { description: "No chart data", style: { padding: 20 }, image: antd.Empty.PRESENTED_IMAGE_SIMPLE });
|
|
21273
|
+
}
|
|
21274
|
+
return /* @__PURE__ */ jsxRuntime.jsx(InlinePlotlyHtml, { html: chartHtml, style: { padding: 8, height: "100%", overflow: "auto" } });
|
|
21275
|
+
};
|
|
21107
21276
|
var DashboardGridCell = ({ cell, allModels, isMaximized, isMinimized, canConfigureLayout, onConfigure, onMaximize, onMinimize, onResize, onMove }) => {
|
|
21108
21277
|
const { token } = antd.theme.useToken();
|
|
21109
21278
|
const model = findModelByName(allModels, cell.model);
|
|
@@ -21136,10 +21305,12 @@ var DashboardGridCell = ({ cell, allModels, isMaximized, isMinimized, canConfigu
|
|
|
21136
21305
|
minHeight: 32,
|
|
21137
21306
|
position: "relative"
|
|
21138
21307
|
};
|
|
21308
|
+
const isPlotlyChart = cell.source_type === "plotly_chart";
|
|
21139
21309
|
const resource = model?.resource || cell.model;
|
|
21140
21310
|
const isModelLike = cell.source_type === "model" || cell.source_type === "named_query";
|
|
21141
|
-
const cellTitle = isModelLike ? model?.label || cell.model : cell.section_name || cell.model;
|
|
21311
|
+
const cellTitle = isPlotlyChart ? cell.chart_title || cell.model : isModelLike ? model?.label || cell.model : cell.section_name || cell.model;
|
|
21142
21312
|
const tone = isModelLike && model ? getModelTone(model) : null;
|
|
21313
|
+
const [chartRefreshNonce, setChartRefreshNonce] = React5.useState(0);
|
|
21143
21314
|
const startResize = React5.useCallback((e, dir) => {
|
|
21144
21315
|
e.preventDefault();
|
|
21145
21316
|
e.stopPropagation();
|
|
@@ -21293,7 +21464,7 @@ var DashboardGridCell = ({ cell, allModels, isMaximized, isMinimized, canConfigu
|
|
|
21293
21464
|
) })
|
|
21294
21465
|
] })
|
|
21295
21466
|
] }),
|
|
21296
|
-
!isMinimized && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { flex: 1, overflow: "auto", minHeight: 0 }, children: model ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
21467
|
+
!isMinimized && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { flex: 1, overflow: "auto", minHeight: 0 }, children: isPlotlyChart && cell.chart_url ? /* @__PURE__ */ jsxRuntime.jsx(PlotlyChartContent, { chartUrl: cell.chart_url, refreshNonce: chartRefreshNonce }) : model ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
21297
21468
|
DynamicList,
|
|
21298
21469
|
{
|
|
21299
21470
|
model,
|
|
@@ -21971,6 +22142,7 @@ var authSystemModels = [
|
|
|
21971
22142
|
|
|
21972
22143
|
exports.API_URL = API_URL3;
|
|
21973
22144
|
exports.AllModelsProvider = AllModelsProvider;
|
|
22145
|
+
exports.AuthenticatedImage = AuthenticatedImage;
|
|
21974
22146
|
exports.ColorModeContext = ColorModeContext;
|
|
21975
22147
|
exports.ColorModeContextProvider = ColorModeContextProvider;
|
|
21976
22148
|
exports.CommandCenterPortal = CommandCenterPortal;
|
|
@@ -22017,6 +22189,7 @@ exports.resolveIcon = resolveIcon;
|
|
|
22017
22189
|
exports.setColorSchemas = setColorSchemas;
|
|
22018
22190
|
exports.sortItemsByNavConfig = sortItemsByNavConfig;
|
|
22019
22191
|
exports.useAllModels = useAllModels;
|
|
22192
|
+
exports.useAuthenticatedFileUrl = useAuthenticatedFileUrl;
|
|
22020
22193
|
exports.useKeyboardShortcuts = useKeyboardShortcuts;
|
|
22021
22194
|
exports.useMetadataModal = useMetadataModal;
|
|
22022
22195
|
exports.useNavConfig = useNavConfig;
|