@juicemantics/veloiq-ui 0.1.0 → 0.2.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 +56 -1
- package/dist/index.d.ts +56 -1
- package/dist/index.js +897 -41
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +896 -44
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -2
package/dist/index.js
CHANGED
|
@@ -866,7 +866,7 @@ var LayoutWrapper = ({
|
|
|
866
866
|
label: "Confirm Password",
|
|
867
867
|
dependencies: ["new_password"],
|
|
868
868
|
rules: [{ required: true }, ({ getFieldValue }) => ({
|
|
869
|
-
validator(
|
|
869
|
+
validator(_39, value) {
|
|
870
870
|
if (!value || getFieldValue("new_password") === value) return Promise.resolve();
|
|
871
871
|
return Promise.reject(new Error("Passwords do not match"));
|
|
872
872
|
}
|
|
@@ -2606,19 +2606,19 @@ function Ut({
|
|
|
2606
2606
|
const { defaultLayoutDeferred: Y, derivedPanelConstraints: Ee, layout: ce } = j.next;
|
|
2607
2607
|
if (Y || Ee.length === 0)
|
|
2608
2608
|
return;
|
|
2609
|
-
const ut = R.panels.map(({ id:
|
|
2610
|
-
R.mutableState.layouts[ut] = ce, Ee.forEach((
|
|
2611
|
-
if (
|
|
2609
|
+
const ut = R.panels.map(({ id: _39 }) => _39).join(",");
|
|
2610
|
+
R.mutableState.layouts[ut] = ce, Ee.forEach((_39) => {
|
|
2611
|
+
if (_39.collapsible) {
|
|
2612
2612
|
const { layout: ge } = j.prev ?? {};
|
|
2613
2613
|
if (ge) {
|
|
2614
2614
|
const ft = I(
|
|
2615
|
-
|
|
2616
|
-
ce[
|
|
2615
|
+
_39.collapsedSize,
|
|
2616
|
+
ce[_39.panelId]
|
|
2617
2617
|
), dt = I(
|
|
2618
|
-
|
|
2619
|
-
ge[
|
|
2618
|
+
_39.collapsedSize,
|
|
2619
|
+
ge[_39.panelId]
|
|
2620
2620
|
);
|
|
2621
|
-
ft && !dt && (R.mutableState.expandedPanelSizes[
|
|
2621
|
+
ft && !dt && (R.mutableState.expandedPanelSizes[_39.panelId] = ge[_39.panelId]);
|
|
2622
2622
|
}
|
|
2623
2623
|
}
|
|
2624
2624
|
});
|
|
@@ -3997,7 +3997,7 @@ var parseInlineStyle = (styleText) => {
|
|
|
3997
3997
|
return styleText.split(";").map((chunk) => chunk.trim()).filter(Boolean).reduce((acc, rule) => {
|
|
3998
3998
|
const [rawKey, rawValue] = rule.split(":").map((part) => part.trim());
|
|
3999
3999
|
if (!rawKey || !rawValue) return acc;
|
|
4000
|
-
const camelKey = rawKey.replace(/-([a-z])/g, (
|
|
4000
|
+
const camelKey = rawKey.replace(/-([a-z])/g, (_39, char) => char.toUpperCase());
|
|
4001
4001
|
acc[camelKey] = rawValue;
|
|
4002
4002
|
return acc;
|
|
4003
4003
|
}, {});
|
|
@@ -6603,6 +6603,68 @@ var RelationsExplorer = ({ model, record, allModels, isActive = true }) => {
|
|
|
6603
6603
|
] })
|
|
6604
6604
|
] });
|
|
6605
6605
|
};
|
|
6606
|
+
|
|
6607
|
+
// src/providers/constants.ts
|
|
6608
|
+
var API_URL3 = "/api";
|
|
6609
|
+
|
|
6610
|
+
// src/pages/dashboard/hooks/usePinRecord.ts
|
|
6611
|
+
function usePinRecord(resource, recordId) {
|
|
6612
|
+
const [pinned, setPinned] = React6.useState(null);
|
|
6613
|
+
const [loading, setLoading] = React6.useState(false);
|
|
6614
|
+
React6.useEffect(() => {
|
|
6615
|
+
if (!resource || recordId === void 0 || recordId === null || recordId === "") return;
|
|
6616
|
+
let cancelled = false;
|
|
6617
|
+
authenticatedFetch(
|
|
6618
|
+
`${API_URL3}/dashboard/pinned-records/check?resource=${encodeURIComponent(resource)}&record_id=${encodeURIComponent(String(recordId))}`
|
|
6619
|
+
).then((r) => r.json()).then((d) => {
|
|
6620
|
+
if (!cancelled) setPinned(Boolean(d.pinned));
|
|
6621
|
+
}).catch(() => {
|
|
6622
|
+
if (!cancelled) setPinned(false);
|
|
6623
|
+
});
|
|
6624
|
+
return () => {
|
|
6625
|
+
cancelled = true;
|
|
6626
|
+
};
|
|
6627
|
+
}, [resource, recordId]);
|
|
6628
|
+
const pin = React6.useCallback(async () => {
|
|
6629
|
+
if (!resource || recordId === void 0) return;
|
|
6630
|
+
setLoading(true);
|
|
6631
|
+
try {
|
|
6632
|
+
await authenticatedFetch(`${API_URL3}/dashboard/pinned-records`, {
|
|
6633
|
+
method: "POST",
|
|
6634
|
+
headers: { "Content-Type": "application/json" },
|
|
6635
|
+
body: JSON.stringify({ resource, record_id: String(recordId) })
|
|
6636
|
+
});
|
|
6637
|
+
setPinned(true);
|
|
6638
|
+
} finally {
|
|
6639
|
+
setLoading(false);
|
|
6640
|
+
}
|
|
6641
|
+
}, [resource, recordId]);
|
|
6642
|
+
const unpin = React6.useCallback(async () => {
|
|
6643
|
+
if (!resource || recordId === void 0) return;
|
|
6644
|
+
setLoading(true);
|
|
6645
|
+
try {
|
|
6646
|
+
await authenticatedFetch(
|
|
6647
|
+
`${API_URL3}/dashboard/pinned-records/${encodeURIComponent(resource)}/${encodeURIComponent(String(recordId))}`,
|
|
6648
|
+
{ method: "DELETE" }
|
|
6649
|
+
);
|
|
6650
|
+
setPinned(false);
|
|
6651
|
+
} finally {
|
|
6652
|
+
setLoading(false);
|
|
6653
|
+
}
|
|
6654
|
+
}, [resource, recordId]);
|
|
6655
|
+
const toggle = React6.useCallback(() => pinned ? unpin() : pin(), [pinned, pin, unpin]);
|
|
6656
|
+
return { pinned, loading, pin, unpin, toggle };
|
|
6657
|
+
}
|
|
6658
|
+
async function unpinRecords(resource, recordIds) {
|
|
6659
|
+
await Promise.all(
|
|
6660
|
+
recordIds.map(
|
|
6661
|
+
(id) => authenticatedFetch(
|
|
6662
|
+
`${API_URL3}/dashboard/pinned-records/${encodeURIComponent(resource)}/${encodeURIComponent(String(id))}`,
|
|
6663
|
+
{ method: "DELETE" }
|
|
6664
|
+
)
|
|
6665
|
+
)
|
|
6666
|
+
);
|
|
6667
|
+
}
|
|
6606
6668
|
var _15 = window._ || ((text) => text);
|
|
6607
6669
|
var useShowActionsPreferences = (model, allModels, record, saveButtonProps) => {
|
|
6608
6670
|
const apiUrl = core.useApiUrl();
|
|
@@ -6712,6 +6774,9 @@ var useShowActionsPreferences = (model, allModels, record, saveButtonProps) => {
|
|
|
6712
6774
|
] });
|
|
6713
6775
|
const { id: urlId } = reactRouterDom.useParams();
|
|
6714
6776
|
const effectiveRecord = record ?? (urlId ? { eid: Number(urlId) } : void 0);
|
|
6777
|
+
const recordId = effectiveRecord?.eid ?? effectiveRecord?.id ?? urlId;
|
|
6778
|
+
const resource = model.resource || model.name;
|
|
6779
|
+
const { pinned, loading: pinLoading, toggle: togglePin } = usePinRecord(resource, recordId);
|
|
6715
6780
|
const { metadataButton, metadataModal } = useMetadataModal(model, allModels);
|
|
6716
6781
|
const [exploreOpen, setExploreOpen] = React6.useState(false);
|
|
6717
6782
|
const headerButtons = ({ defaultButtons }) => /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
@@ -6719,6 +6784,15 @@ var useShowActionsPreferences = (model, allModels, record, saveButtonProps) => {
|
|
|
6719
6784
|
metadataModal,
|
|
6720
6785
|
/* @__PURE__ */ jsxRuntime.jsx(antd.Popover, { content: actionsSettingsContent, title: _15("Actions"), trigger: "hover", children: /* @__PURE__ */ jsxRuntime.jsx(antd.Button, { size: "small", icon: /* @__PURE__ */ jsxRuntime.jsx(icons.SettingOutlined, {}) }) }),
|
|
6721
6786
|
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { marginInlineStart: 10 } }),
|
|
6787
|
+
pinned !== null && /* @__PURE__ */ jsxRuntime.jsx(antd.Tooltip, { title: pinned ? _15("Unpin") : _15("Pin to dashboard"), children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
6788
|
+
antd.Button,
|
|
6789
|
+
{
|
|
6790
|
+
size: "small",
|
|
6791
|
+
icon: pinned ? /* @__PURE__ */ jsxRuntime.jsx(icons.PushpinFilled, { style: { color: "#faad14" } }) : /* @__PURE__ */ jsxRuntime.jsx(icons.PushpinOutlined, {}),
|
|
6792
|
+
onClick: togglePin,
|
|
6793
|
+
loading: pinLoading
|
|
6794
|
+
}
|
|
6795
|
+
) }),
|
|
6722
6796
|
/* @__PURE__ */ jsxRuntime.jsx(antd.Tooltip, { title: _15("Explore"), children: /* @__PURE__ */ jsxRuntime.jsx(antd.Button, { size: "small", icon: /* @__PURE__ */ jsxRuntime.jsx(icons.ApartmentOutlined, {}), onClick: () => setExploreOpen(true) }) }),
|
|
6723
6797
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6724
6798
|
antd.Modal,
|
|
@@ -7665,7 +7739,7 @@ var DynamicCreate = ({ model: modelProp, allModels, journeyCallbacks, injectedVa
|
|
|
7665
7739
|
const prefix = useReadonly ? "pc" : "cr";
|
|
7666
7740
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { border: `1px solid ${token.colorBorder}`, borderRadius: 8, padding: "6px 6px", marginBottom: 6 }, children: [
|
|
7667
7741
|
/* @__PURE__ */ jsxRuntime.jsx(Title2, { level: 5, style: { margin: 0, marginBottom: 6, color: "#1677ff" }, children: _23(section) }),
|
|
7668
|
-
/* @__PURE__ */ jsxRuntime.jsx("table", { style: { width: "100%", borderCollapse: "collapse", tableLayout: "fixed" }, children: /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: Array.from({ length: maxRow }).map((
|
|
7742
|
+
/* @__PURE__ */ jsxRuntime.jsx("table", { style: { width: "100%", borderCollapse: "collapse", tableLayout: "fixed" }, children: /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: Array.from({ length: maxRow }).map((_39, rowIdx) => /* @__PURE__ */ jsxRuntime.jsx("tr", { children: Array.from({ length: maxCol }).map((_40, colIdx) => {
|
|
7669
7743
|
const cellItems = normalized.filter((r) => r.row === rowIdx + 1 && r.column === colIdx + 1);
|
|
7670
7744
|
return /* @__PURE__ */ jsxRuntime.jsx("td", { style: { padding: "0 4px", verticalAlign: "top", width: `${100 / maxCol}%` }, children: cellItems.map(
|
|
7671
7745
|
(item, idx) => useReadonly ? renderReadonlyCell(item, idx) : renderFormCell(item, idx)
|
|
@@ -8285,7 +8359,7 @@ var DynamicEdit = ({ model: modelProp, allModels, topContent, extraHeaderButtons
|
|
|
8285
8359
|
},
|
|
8286
8360
|
children: [
|
|
8287
8361
|
/* @__PURE__ */ jsxRuntime.jsx(Title3, { level: 5, style: { margin: 0, color: "#1677ff" }, children: _24(section) }),
|
|
8288
|
-
/* @__PURE__ */ jsxRuntime.jsx("table", { style: { width: "100%", borderCollapse: "collapse", tableLayout: "fixed" }, children: /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: Array.from({ length: maxRow }).map((
|
|
8362
|
+
/* @__PURE__ */ jsxRuntime.jsx("table", { style: { width: "100%", borderCollapse: "collapse", tableLayout: "fixed" }, children: /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: Array.from({ length: maxRow }).map((_39, rowIndex) => /* @__PURE__ */ jsxRuntime.jsx("tr", { children: Array.from({ length: maxCol }).map((_40, colIndex) => {
|
|
8289
8363
|
const cellItems = normalized.filter(
|
|
8290
8364
|
(item) => item.row === rowIndex + 1 && item.column === colIndex + 1
|
|
8291
8365
|
);
|
|
@@ -8498,7 +8572,7 @@ var DynamicEdit = ({ model: modelProp, allModels, topContent, extraHeaderButtons
|
|
|
8498
8572
|
const maxCol = Math.max(1, ...normalized.map((r) => r.column));
|
|
8499
8573
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { flex: 1, minWidth: 0, border: `1px solid ${token.colorBorder}`, borderRadius: 8, padding: "2px 6px" }, children: [
|
|
8500
8574
|
/* @__PURE__ */ jsxRuntime.jsx(Title3, { level: 5, style: { margin: 0, color: "#1677ff" }, children: _24(section) }),
|
|
8501
|
-
/* @__PURE__ */ jsxRuntime.jsx("table", { style: { width: "100%", borderCollapse: "collapse", tableLayout: "fixed" }, children: /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: Array.from({ length: maxRow }).map((
|
|
8575
|
+
/* @__PURE__ */ jsxRuntime.jsx("table", { style: { width: "100%", borderCollapse: "collapse", tableLayout: "fixed" }, children: /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: Array.from({ length: maxRow }).map((_39, ri) => /* @__PURE__ */ jsxRuntime.jsx("tr", { children: Array.from({ length: maxCol }).map((_40, ci) => {
|
|
8502
8576
|
const cellItems = normalized.filter((item) => item.row === ri + 1 && item.column === ci + 1);
|
|
8503
8577
|
return /* @__PURE__ */ jsxRuntime.jsx("td", { style: { padding: "0 4px", verticalAlign: "top", width: `${100 / maxCol}%` }, children: cellItems.map((item, idx) => {
|
|
8504
8578
|
if (item.attribute_or_relation_type === "nlsentence") {
|
|
@@ -8819,7 +8893,7 @@ var useStandardShowTabs = (model, record, allModels, actionsState, editForm, ove
|
|
|
8819
8893
|
},
|
|
8820
8894
|
children: [
|
|
8821
8895
|
/* @__PURE__ */ jsxRuntime.jsx(Title4, { level: 5, style: { margin: 0, color: "#1677ff" }, children: _25(section) }),
|
|
8822
|
-
/* @__PURE__ */ jsxRuntime.jsx("table", { style: { width: "100%", borderCollapse: "collapse", tableLayout: "fixed" }, children: /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: Array.from({ length: maxRow }).map((
|
|
8896
|
+
/* @__PURE__ */ jsxRuntime.jsx("table", { style: { width: "100%", borderCollapse: "collapse", tableLayout: "fixed" }, children: /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: Array.from({ length: maxRow }).map((_39, rowIndex) => /* @__PURE__ */ jsxRuntime.jsx("tr", { children: Array.from({ length: maxCol }).map((_40, colIndex) => {
|
|
8823
8897
|
const cellItems = normalized.filter(
|
|
8824
8898
|
(item) => item.row === rowIndex + 1 && item.column === colIndex + 1
|
|
8825
8899
|
);
|
|
@@ -9012,7 +9086,7 @@ var useStandardShowTabs = (model, record, allModels, actionsState, editForm, ove
|
|
|
9012
9086
|
const maxCol = Math.max(1, ...normalized.map((r) => r.column));
|
|
9013
9087
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { flex: 1, minWidth: 0, border: `1px solid ${token.colorBorder}`, borderRadius: 8, padding: "6px 6px" }, children: [
|
|
9014
9088
|
/* @__PURE__ */ jsxRuntime.jsx(Title4, { level: 5, style: { margin: 0, color: "#1677ff" }, children: _25(section) }),
|
|
9015
|
-
/* @__PURE__ */ jsxRuntime.jsx("table", { style: { width: "100%", borderCollapse: "collapse", tableLayout: "fixed" }, children: /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: Array.from({ length: maxRow }).map((
|
|
9089
|
+
/* @__PURE__ */ jsxRuntime.jsx("table", { style: { width: "100%", borderCollapse: "collapse", tableLayout: "fixed" }, children: /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: Array.from({ length: maxRow }).map((_39, ri) => /* @__PURE__ */ jsxRuntime.jsx("tr", { children: Array.from({ length: maxCol }).map((_40, ci) => {
|
|
9016
9090
|
const cellItems = normalized.filter((item) => item.row === ri + 1 && item.column === ci + 1);
|
|
9017
9091
|
return /* @__PURE__ */ jsxRuntime.jsx("td", { style: { padding: "0 4px", verticalAlign: "top", width: `${100 / maxCol}%` }, children: cellItems.map((item) => {
|
|
9018
9092
|
if (item.attribute_or_relation_type === "nlsentence") {
|
|
@@ -9917,7 +9991,7 @@ var RelatedObjectsEditableList = ({ rel, record, allModels }) => {
|
|
|
9917
9991
|
setPage(p);
|
|
9918
9992
|
}
|
|
9919
9993
|
},
|
|
9920
|
-
onShowSizeChange: (
|
|
9994
|
+
onShowSizeChange: (_39, newPageSize) => {
|
|
9921
9995
|
setPageSize(newPageSize);
|
|
9922
9996
|
setPage(1);
|
|
9923
9997
|
},
|
|
@@ -12377,7 +12451,7 @@ var RelatedObjectsTable = ({ rel, record, relatedModel, parentModel, showActions
|
|
|
12377
12451
|
setCurrentPage(1);
|
|
12378
12452
|
}
|
|
12379
12453
|
},
|
|
12380
|
-
onShowSizeChange: (
|
|
12454
|
+
onShowSizeChange: (_39, newPageSize) => {
|
|
12381
12455
|
if (newPageSize && newPageSize !== pageSize) {
|
|
12382
12456
|
setPageSize(newPageSize);
|
|
12383
12457
|
setCurrentPage(1);
|
|
@@ -12387,7 +12461,7 @@ var RelatedObjectsTable = ({ rel, record, relatedModel, parentModel, showActions
|
|
|
12387
12461
|
size: "small",
|
|
12388
12462
|
rowKey: (row) => row?.__relationKey || row?.eid || row?.id || JSON.stringify(row),
|
|
12389
12463
|
locale: filteredRows.length === 0 ? { emptyText: /* @__PURE__ */ jsxRuntime.jsx("span", { style: { display: "inline-block", fontSize: 12, color: "#8c8c8c" }, children: _30("No related records") }) } : void 0,
|
|
12390
|
-
onChange: (
|
|
12464
|
+
onChange: (_39, filters, sorter, extra) => {
|
|
12391
12465
|
const nextFilters = {};
|
|
12392
12466
|
Object.entries(filters || {}).forEach(([key, values]) => {
|
|
12393
12467
|
if (!values) return;
|
|
@@ -13748,7 +13822,7 @@ var renderRelationBlock = ({
|
|
|
13748
13822
|
};
|
|
13749
13823
|
var _32 = window._ || ((text) => text);
|
|
13750
13824
|
var { Title: Title7 } = antd.Typography;
|
|
13751
|
-
var DynamicList = ({ model: modelProp, allModels, filter, relationConfig, isEmbedded = false, showActions = true, showCreate = true, layoutPreferenceType, listViewType, rowSelection, extraHeaderButtons, bulkActions }) => {
|
|
13825
|
+
var DynamicList = ({ model: modelProp, allModels, filter, relationConfig, isEmbedded = false, showActions = true, showCreate = true, layoutPreferenceType, listViewType, rowSelection, extraHeaderButtons, bulkActions, preferencesResourceOverride, defaultListVisible }) => {
|
|
13752
13826
|
const model = useRoleFilteredModel(modelProp);
|
|
13753
13827
|
applyI18nLabelsToModel(model);
|
|
13754
13828
|
applyI18nLabelsToModels(allModels);
|
|
@@ -13759,6 +13833,7 @@ var DynamicList = ({ model: modelProp, allModels, filter, relationConfig, isEmbe
|
|
|
13759
13833
|
const invalidate = core.useInvalidate();
|
|
13760
13834
|
const apiUrl = core.useApiUrl();
|
|
13761
13835
|
const resourceIdentifier = resolveResourcePath(model.resource || model.name, allModels);
|
|
13836
|
+
const prefsKey = preferencesResourceOverride ?? resourceIdentifier;
|
|
13762
13837
|
const { data: canDeleteData } = core.useCan({ resource: resourceIdentifier, action: "delete" });
|
|
13763
13838
|
const { data: canEditData } = core.useCan({ resource: resourceIdentifier, action: "edit" });
|
|
13764
13839
|
const canBulkDelete = canDeleteData?.can !== false;
|
|
@@ -13809,7 +13884,7 @@ var DynamicList = ({ model: modelProp, allModels, filter, relationConfig, isEmbe
|
|
|
13809
13884
|
const galleryImageHeight = viewSettings?.galleryImageHeight ?? 140;
|
|
13810
13885
|
const calendarDateFieldOptions = React6.useMemo(() => getCalendarDateFieldOptions(model.fields), [model.fields]);
|
|
13811
13886
|
const [localSearch, setLocalSearch] = React6.useState("");
|
|
13812
|
-
const [listVisible, setListVisible] = React6.useState(true);
|
|
13887
|
+
const [listVisible, setListVisible] = React6.useState(defaultListVisible ?? true);
|
|
13813
13888
|
const [isTdFlipped, setIsTdFlipped] = React6.useState(false);
|
|
13814
13889
|
const [pageSize, setPageSize] = React6.useState(10);
|
|
13815
13890
|
const [galleryPage, setGalleryPage] = React6.useState(1);
|
|
@@ -14418,7 +14493,7 @@ var DynamicList = ({ model: modelProp, allModels, filter, relationConfig, isEmbe
|
|
|
14418
14493
|
}
|
|
14419
14494
|
}, [numericFields, rankingFieldKey, rankingMode]);
|
|
14420
14495
|
const resetLayoutDefaults = React6.useCallback(() => {
|
|
14421
|
-
setListVisible(true);
|
|
14496
|
+
setListVisible(defaultListVisible ?? true);
|
|
14422
14497
|
setAnalyzeOpen(false);
|
|
14423
14498
|
setIsAnalyzeVertical(false);
|
|
14424
14499
|
setIsAnalyzeFirst(false);
|
|
@@ -14427,7 +14502,7 @@ var DynamicList = ({ model: modelProp, allModels, filter, relationConfig, isEmbe
|
|
|
14427
14502
|
setSelectedColumnKeys(null);
|
|
14428
14503
|
setColumnOrder(null);
|
|
14429
14504
|
setTotalsSummaryFunctions({});
|
|
14430
|
-
}, [isEmbedded]);
|
|
14505
|
+
}, [isEmbedded, defaultListVisible]);
|
|
14431
14506
|
const resetAnalyzeDefaults = React6.useCallback(() => {
|
|
14432
14507
|
setCategoryField1(categoricalFields[0]?.key ?? null);
|
|
14433
14508
|
setCategoryField2(categoricalFields.length > 1 ? categoricalFields[1].key : null);
|
|
@@ -14440,7 +14515,7 @@ var DynamicList = ({ model: modelProp, allModels, filter, relationConfig, isEmbe
|
|
|
14440
14515
|
}, [categoricalFields, numericFields]);
|
|
14441
14516
|
const persistCurrentViewNames = React6.useCallback(async (nextSelected, nextCurrent) => {
|
|
14442
14517
|
try {
|
|
14443
|
-
const resourceKey =
|
|
14518
|
+
const resourceKey = prefsKey;
|
|
14444
14519
|
await authenticatedFetch(`${apiUrl}/views/preferences/view`, {
|
|
14445
14520
|
method: "POST",
|
|
14446
14521
|
headers: { "Content-Type": "application/json" },
|
|
@@ -14453,9 +14528,9 @@ var DynamicList = ({ model: modelProp, allModels, filter, relationConfig, isEmbe
|
|
|
14453
14528
|
});
|
|
14454
14529
|
} catch {
|
|
14455
14530
|
}
|
|
14456
|
-
}, [apiUrl, model.name, model.resource, allModels]);
|
|
14531
|
+
}, [apiUrl, model.name, model.resource, allModels, preferencesResourceOverride]);
|
|
14457
14532
|
const loadViewNames = React6.useCallback(async () => {
|
|
14458
|
-
const resourceKey =
|
|
14533
|
+
const resourceKey = prefsKey;
|
|
14459
14534
|
setIsLoadingViewNames(true);
|
|
14460
14535
|
try {
|
|
14461
14536
|
const response = await authenticatedFetch(`${apiUrl}/views/preferences?resource=${encodeURIComponent(resourceKey)}&preference_type=__all__`);
|
|
@@ -14498,7 +14573,7 @@ var DynamicList = ({ model: modelProp, allModels, filter, relationConfig, isEmbe
|
|
|
14498
14573
|
setViewNamesLoaded(true);
|
|
14499
14574
|
setIsLoadingViewNames(false);
|
|
14500
14575
|
}
|
|
14501
|
-
}, [apiUrl, model.name, model.resource, allModels]);
|
|
14576
|
+
}, [apiUrl, model.name, model.resource, allModels, preferencesResourceOverride]);
|
|
14502
14577
|
const openSaveViewModalFor = React6.useCallback((target) => {
|
|
14503
14578
|
setSaveViewName(currentViewName || getDefaultViewName());
|
|
14504
14579
|
setSaveViewAsNew(false);
|
|
@@ -14547,7 +14622,7 @@ var DynamicList = ({ model: modelProp, allModels, filter, relationConfig, isEmbe
|
|
|
14547
14622
|
return;
|
|
14548
14623
|
}
|
|
14549
14624
|
try {
|
|
14550
|
-
const resourceKey =
|
|
14625
|
+
const resourceKey = prefsKey;
|
|
14551
14626
|
const response = await authenticatedFetch(`${apiUrl}/views/preferences/view`, {
|
|
14552
14627
|
method: "POST",
|
|
14553
14628
|
headers: { "Content-Type": "application/json" },
|
|
@@ -14571,7 +14646,7 @@ var DynamicList = ({ model: modelProp, allModels, filter, relationConfig, isEmbe
|
|
|
14571
14646
|
okButtonProps: { danger: true },
|
|
14572
14647
|
onOk: async () => {
|
|
14573
14648
|
try {
|
|
14574
|
-
const resourceKey =
|
|
14649
|
+
const resourceKey = prefsKey;
|
|
14575
14650
|
const response = await authenticatedFetch(`${apiUrl}/views/preferences/view`, {
|
|
14576
14651
|
method: "POST",
|
|
14577
14652
|
headers: { "Content-Type": "application/json" },
|
|
@@ -14590,7 +14665,7 @@ var DynamicList = ({ model: modelProp, allModels, filter, relationConfig, isEmbe
|
|
|
14590
14665
|
}, [apiUrl, currentViewName, model.name, model.resource, allModels, loadViewNames]);
|
|
14591
14666
|
const persistLayoutPreferences = React6.useCallback(async (viewName) => {
|
|
14592
14667
|
if (!resolvedLayoutPreferenceType) return;
|
|
14593
|
-
const resourceKey =
|
|
14668
|
+
const resourceKey = prefsKey;
|
|
14594
14669
|
const resolvedViewName = normalizeViewName(viewName);
|
|
14595
14670
|
const preferences = {
|
|
14596
14671
|
listVisible,
|
|
@@ -14631,9 +14706,9 @@ var DynamicList = ({ model: modelProp, allModels, filter, relationConfig, isEmbe
|
|
|
14631
14706
|
} finally {
|
|
14632
14707
|
setIsSavingLayoutPrefs(false);
|
|
14633
14708
|
}
|
|
14634
|
-
}, [apiUrl, analyzeOpen, columnFiltersSelected, columnOrder, columnSort, filtersCollapsed, filterRules, isAnalyzeFirst, isAnalyzeVertical, resolvedLayoutPreferenceType, listVisible, pageSize, selectedColumnKeys, totalsSummaryFunctions, model.name, model.resource, allModels]);
|
|
14709
|
+
}, [apiUrl, analyzeOpen, columnFiltersSelected, columnOrder, columnSort, filtersCollapsed, filterRules, isAnalyzeFirst, isAnalyzeVertical, resolvedLayoutPreferenceType, listVisible, pageSize, selectedColumnKeys, totalsSummaryFunctions, model.name, model.resource, allModels, preferencesResourceOverride]);
|
|
14635
14710
|
const persistAnalyzePreferences = React6.useCallback(async (viewName) => {
|
|
14636
|
-
const resourceKey =
|
|
14711
|
+
const resourceKey = prefsKey;
|
|
14637
14712
|
const resolvedViewName = normalizeViewName(viewName);
|
|
14638
14713
|
const preferences = {
|
|
14639
14714
|
categoryField1,
|
|
@@ -14662,7 +14737,7 @@ var DynamicList = ({ model: modelProp, allModels, filter, relationConfig, isEmbe
|
|
|
14662
14737
|
} finally {
|
|
14663
14738
|
setIsSavingAnalyzePrefs(false);
|
|
14664
14739
|
}
|
|
14665
|
-
}, [apiUrl, categoryField1, categoryField2, chartType, selectedSeriesKeys, summaryFn, rankingMode, rankingFieldKey, rankingN, model.name, model.resource, allModels]);
|
|
14740
|
+
}, [apiUrl, categoryField1, categoryField2, chartType, selectedSeriesKeys, summaryFn, rankingMode, rankingFieldKey, rankingN, model.name, model.resource, allModels, preferencesResourceOverride]);
|
|
14666
14741
|
const handleConfirmSaveView = React6.useCallback(async () => {
|
|
14667
14742
|
if (!pendingSaveTarget) return;
|
|
14668
14743
|
const viewName = normalizeViewName(saveViewName || currentViewName);
|
|
@@ -14706,7 +14781,7 @@ var DynamicList = ({ model: modelProp, allModels, filter, relationConfig, isEmbe
|
|
|
14706
14781
|
resetAnalyzeDefaults();
|
|
14707
14782
|
}, [currentViewName, resetAnalyzeDefaults, resetLayoutDefaults, viewNamesLoaded]);
|
|
14708
14783
|
React6.useEffect(() => {
|
|
14709
|
-
const resourceKey =
|
|
14784
|
+
const resourceKey = prefsKey;
|
|
14710
14785
|
const viewKey = `${resourceKey}::${currentViewName}`;
|
|
14711
14786
|
if (analyzePrefsResourceRef.current !== viewKey) {
|
|
14712
14787
|
analyzePrefsLoadedRef.current = false;
|
|
@@ -14754,10 +14829,10 @@ var DynamicList = ({ model: modelProp, allModels, filter, relationConfig, isEmbe
|
|
|
14754
14829
|
return () => {
|
|
14755
14830
|
cancelled = true;
|
|
14756
14831
|
};
|
|
14757
|
-
}, [apiUrl, currentViewName, model.name, model.resource, allModels]);
|
|
14832
|
+
}, [apiUrl, currentViewName, model.name, model.resource, allModels, preferencesResourceOverride]);
|
|
14758
14833
|
React6.useEffect(() => {
|
|
14759
14834
|
if (!resolvedLayoutPreferenceType) return;
|
|
14760
|
-
const resourceKey =
|
|
14835
|
+
const resourceKey = prefsKey;
|
|
14761
14836
|
const viewKey = `${resourceKey}::${resolvedLayoutPreferenceType}::${currentViewName}`;
|
|
14762
14837
|
if (layoutPrefsResourceRef.current !== viewKey) {
|
|
14763
14838
|
layoutPrefsLoadedRef.current = false;
|
|
@@ -14771,7 +14846,7 @@ var DynamicList = ({ model: modelProp, allModels, filter, relationConfig, isEmbe
|
|
|
14771
14846
|
let cancelled = false;
|
|
14772
14847
|
const applyPrefs = (prefs) => {
|
|
14773
14848
|
if (!prefs || typeof prefs !== "object") return false;
|
|
14774
|
-
if ("listVisible" in prefs) setListVisible(Boolean(prefs.listVisible));
|
|
14849
|
+
if ("listVisible" in prefs && defaultListVisible !== false) setListVisible(Boolean(prefs.listVisible));
|
|
14775
14850
|
if ("analyzeOpen" in prefs) setAnalyzeOpen(Boolean(prefs.analyzeOpen));
|
|
14776
14851
|
if ("isAnalyzeVertical" in prefs) setIsAnalyzeVertical(Boolean(prefs.isAnalyzeVertical));
|
|
14777
14852
|
if ("isAnalyzeFirst" in prefs) setIsAnalyzeFirst(Boolean(prefs.isAnalyzeFirst));
|
|
@@ -14839,7 +14914,7 @@ var DynamicList = ({ model: modelProp, allModels, filter, relationConfig, isEmbe
|
|
|
14839
14914
|
return () => {
|
|
14840
14915
|
cancelled = true;
|
|
14841
14916
|
};
|
|
14842
|
-
}, [apiUrl, currentViewName, resolvedLayoutPreferenceType, model.name, model.resource, allModels]);
|
|
14917
|
+
}, [apiUrl, currentViewName, resolvedLayoutPreferenceType, model.name, model.resource, allModels, preferencesResourceOverride]);
|
|
14843
14918
|
const fetchAllRows = React6.useCallback(async () => {
|
|
14844
14919
|
setIsAllRowsLoading(true);
|
|
14845
14920
|
setAllRowsError(null);
|
|
@@ -15525,6 +15600,17 @@ var DynamicList = ({ model: modelProp, allModels, filter, relationConfig, isEmbe
|
|
|
15525
15600
|
body: JSON.stringify(clonePayload)
|
|
15526
15601
|
});
|
|
15527
15602
|
if (!resp.ok) throw new Error(`${_32("Clone failed for record")} ${id}`);
|
|
15603
|
+
} else if (actionKey === "__pin__") {
|
|
15604
|
+
await authenticatedFetch(`${apiUrl}/dashboard/pinned-records`, {
|
|
15605
|
+
method: "POST",
|
|
15606
|
+
headers: { "Content-Type": "application/json" },
|
|
15607
|
+
body: JSON.stringify({ resource, record_id: String(id) })
|
|
15608
|
+
});
|
|
15609
|
+
} else if (actionKey === "__unpin__") {
|
|
15610
|
+
await authenticatedFetch(
|
|
15611
|
+
`${apiUrl}/dashboard/pinned-records/${encodeURIComponent(resource)}/${encodeURIComponent(String(id))}`,
|
|
15612
|
+
{ method: "DELETE" }
|
|
15613
|
+
);
|
|
15528
15614
|
} else {
|
|
15529
15615
|
const customAction = bulkActions?.find((a) => a.key === actionKey);
|
|
15530
15616
|
if (customAction) await customAction.onExecuteOne(record);
|
|
@@ -15612,6 +15698,8 @@ var DynamicList = ({ model: modelProp, allModels, filter, relationConfig, isEmbe
|
|
|
15612
15698
|
if (bulkActions && bulkActions.length > 0) {
|
|
15613
15699
|
bulkActions.forEach((a) => opts.push({ label: _32(a.label), value: a.key }));
|
|
15614
15700
|
}
|
|
15701
|
+
opts.push({ label: _32("Pin selected"), value: "__pin__" });
|
|
15702
|
+
opts.push({ label: _32("Unpin selected"), value: "__unpin__" });
|
|
15615
15703
|
if (canBulkDelete) {
|
|
15616
15704
|
opts.push({ label: _32("Delete selected"), value: "__delete__" });
|
|
15617
15705
|
}
|
|
@@ -17588,7 +17676,7 @@ var MultiPaneLayout = ({ children }) => {
|
|
|
17588
17676
|
[openDetail]
|
|
17589
17677
|
);
|
|
17590
17678
|
const detailPaneContexts = React6.useMemo(
|
|
17591
|
-
() => panes.map((
|
|
17679
|
+
() => panes.map((_39, idx) => ({
|
|
17592
17680
|
isInMultiPane: true,
|
|
17593
17681
|
paneIndex: idx + 1,
|
|
17594
17682
|
openDetail: (resource, id) => openDetail(idx + 1, resource, id)
|
|
@@ -17998,9 +18086,6 @@ httpClient.interceptors.request.use((config) => {
|
|
|
17998
18086
|
}
|
|
17999
18087
|
return config;
|
|
18000
18088
|
});
|
|
18001
|
-
|
|
18002
|
-
// src/providers/constants.ts
|
|
18003
|
-
var API_URL3 = "/api";
|
|
18004
18089
|
var API_BASE_URL = "/api";
|
|
18005
18090
|
var ColorModeContextProvider = ({
|
|
18006
18091
|
children
|
|
@@ -18170,6 +18255,773 @@ var LoginPage = ({ appTitle = "VeloIQ", logo }) => {
|
|
|
18170
18255
|
}
|
|
18171
18256
|
);
|
|
18172
18257
|
};
|
|
18258
|
+
function useDashboardConfig() {
|
|
18259
|
+
const apiUrl = core.useApiUrl();
|
|
18260
|
+
const [config, setConfig] = React6.useState(null);
|
|
18261
|
+
const [enabled, setEnabled] = React6.useState(false);
|
|
18262
|
+
const [loading, setLoading] = React6.useState(true);
|
|
18263
|
+
const load = React6.useCallback(async () => {
|
|
18264
|
+
setLoading(true);
|
|
18265
|
+
try {
|
|
18266
|
+
const res = await authenticatedFetch(`${apiUrl}/dashboard/config`);
|
|
18267
|
+
if (!res.ok) {
|
|
18268
|
+
setLoading(false);
|
|
18269
|
+
return;
|
|
18270
|
+
}
|
|
18271
|
+
const data = await res.json();
|
|
18272
|
+
setEnabled(Boolean(data.enabled));
|
|
18273
|
+
if (data.enabled && data.dashboard) {
|
|
18274
|
+
setConfig(data.dashboard);
|
|
18275
|
+
}
|
|
18276
|
+
} catch {
|
|
18277
|
+
} finally {
|
|
18278
|
+
setLoading(false);
|
|
18279
|
+
}
|
|
18280
|
+
}, [apiUrl]);
|
|
18281
|
+
React6.useEffect(() => {
|
|
18282
|
+
load();
|
|
18283
|
+
}, [load]);
|
|
18284
|
+
const save = React6.useCallback(async (next) => {
|
|
18285
|
+
setConfig(next);
|
|
18286
|
+
try {
|
|
18287
|
+
await authenticatedFetch(`${apiUrl}/dashboard/config`, {
|
|
18288
|
+
method: "PUT",
|
|
18289
|
+
headers: { "Content-Type": "application/json" },
|
|
18290
|
+
body: JSON.stringify({ dashboard: next })
|
|
18291
|
+
});
|
|
18292
|
+
} catch {
|
|
18293
|
+
}
|
|
18294
|
+
}, [apiUrl]);
|
|
18295
|
+
return { config, enabled, loading, save, reload: load };
|
|
18296
|
+
}
|
|
18297
|
+
var { Text } = antd.Typography;
|
|
18298
|
+
var VIEW_TYPE_OPTIONS = [
|
|
18299
|
+
{ label: "Default (from model schema)", value: "" },
|
|
18300
|
+
{ label: "Table", value: "table" },
|
|
18301
|
+
{ label: "Gallery", value: "gallery" },
|
|
18302
|
+
{ label: "Calendar", value: "calendar" },
|
|
18303
|
+
{ label: "Totals / Details", value: "totals-details" }
|
|
18304
|
+
];
|
|
18305
|
+
var nextGridPosition = (cells) => {
|
|
18306
|
+
if (!cells.length) return { row: 0, col: 0 };
|
|
18307
|
+
const maxRow = Math.max(...cells.map((c) => c.row));
|
|
18308
|
+
const lastRowCells = cells.filter((c) => c.row === maxRow);
|
|
18309
|
+
if (lastRowCells.length < 2) return { row: maxRow, col: lastRowCells.length };
|
|
18310
|
+
return { row: maxRow + 1, col: 0 };
|
|
18311
|
+
};
|
|
18312
|
+
var CellConfigDrawer = ({ open, cell, tabId, config, onClose, onSave }) => {
|
|
18313
|
+
const [form] = antd.Form.useForm();
|
|
18314
|
+
React6.useEffect(() => {
|
|
18315
|
+
if (!cell || !tabId) return;
|
|
18316
|
+
const tab = config.tabs.find((t) => t.id === tabId);
|
|
18317
|
+
form.setFieldsValue({
|
|
18318
|
+
tabName: tab?.name ?? "",
|
|
18319
|
+
row: cell.row + 1,
|
|
18320
|
+
col: cell.col + 1,
|
|
18321
|
+
view_type: cell.view_type ?? "",
|
|
18322
|
+
html_style: cell.html_style ?? "",
|
|
18323
|
+
min_width: cell.min_width ?? "",
|
|
18324
|
+
max_width: cell.max_width ?? "",
|
|
18325
|
+
min_height: cell.min_height ?? "",
|
|
18326
|
+
max_height: cell.max_height ?? ""
|
|
18327
|
+
});
|
|
18328
|
+
}, [cell, tabId, config, form]);
|
|
18329
|
+
const handleSave = () => {
|
|
18330
|
+
if (!cell || !tabId) return;
|
|
18331
|
+
const values = form.getFieldsValue();
|
|
18332
|
+
const newTabName = (values.tabName || "").trim() || config.tabs.find((t) => t.id === tabId)?.name || "";
|
|
18333
|
+
const updatedCell = {
|
|
18334
|
+
...cell,
|
|
18335
|
+
row: Math.max(0, (values.row ?? 1) - 1),
|
|
18336
|
+
col: Math.max(0, (values.col ?? 1) - 1),
|
|
18337
|
+
view_type: values.view_type || null,
|
|
18338
|
+
html_style: values.html_style ?? "",
|
|
18339
|
+
min_width: values.min_width || null,
|
|
18340
|
+
max_width: values.max_width || null,
|
|
18341
|
+
min_height: values.min_height || null,
|
|
18342
|
+
max_height: values.max_height || null
|
|
18343
|
+
};
|
|
18344
|
+
const currentTab = config.tabs.find((t) => t.id === tabId);
|
|
18345
|
+
const nameUnchanged = currentTab?.name.trim().toLowerCase() === newTabName.toLowerCase();
|
|
18346
|
+
const targetTab = !nameUnchanged ? config.tabs.find((t) => t.id !== tabId && t.name.trim().toLowerCase() === newTabName.toLowerCase()) : void 0;
|
|
18347
|
+
let nextTabs;
|
|
18348
|
+
if (nameUnchanged) {
|
|
18349
|
+
nextTabs = config.tabs.map((tab) => {
|
|
18350
|
+
if (tab.id !== tabId) return tab;
|
|
18351
|
+
return { ...tab, cells: tab.cells.map((c) => c.id === cell.id ? updatedCell : c) };
|
|
18352
|
+
});
|
|
18353
|
+
} else if (targetTab) {
|
|
18354
|
+
const { row, col } = nextGridPosition(targetTab.cells);
|
|
18355
|
+
const repositionedCell = { ...updatedCell, row, col };
|
|
18356
|
+
nextTabs = config.tabs.map((tab) => {
|
|
18357
|
+
if (tab.id === tabId) {
|
|
18358
|
+
return { ...tab, cells: tab.cells.filter((c) => c.id !== cell.id) };
|
|
18359
|
+
}
|
|
18360
|
+
if (tab.id === targetTab.id) {
|
|
18361
|
+
return { ...tab, cells: [...tab.cells, repositionedCell] };
|
|
18362
|
+
}
|
|
18363
|
+
return tab;
|
|
18364
|
+
}).filter((tab) => tab.cells.length > 0);
|
|
18365
|
+
} else {
|
|
18366
|
+
const { row, col } = nextGridPosition([]);
|
|
18367
|
+
const repositionedCell = { ...updatedCell, row, col };
|
|
18368
|
+
const newTab = {
|
|
18369
|
+
id: crypto.randomUUID(),
|
|
18370
|
+
name: newTabName,
|
|
18371
|
+
module: currentTab?.module ?? "dashboard",
|
|
18372
|
+
cells: [repositionedCell]
|
|
18373
|
+
};
|
|
18374
|
+
nextTabs = [
|
|
18375
|
+
...config.tabs.map((tab) => {
|
|
18376
|
+
if (tab.id !== tabId) return tab;
|
|
18377
|
+
return { ...tab, cells: tab.cells.filter((c) => c.id !== cell.id) };
|
|
18378
|
+
}).filter((tab) => tab.cells.length > 0),
|
|
18379
|
+
newTab
|
|
18380
|
+
];
|
|
18381
|
+
}
|
|
18382
|
+
onSave({ ...config, tabs: nextTabs });
|
|
18383
|
+
onClose();
|
|
18384
|
+
};
|
|
18385
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
18386
|
+
antd.Drawer,
|
|
18387
|
+
{
|
|
18388
|
+
title: `Configure cell: ${cell?.model ?? ""}`,
|
|
18389
|
+
placement: "right",
|
|
18390
|
+
width: 380,
|
|
18391
|
+
open,
|
|
18392
|
+
onClose,
|
|
18393
|
+
footer: /* @__PURE__ */ jsxRuntime.jsxs(antd.Space, { style: { justifyContent: "flex-end", width: "100%", display: "flex" }, children: [
|
|
18394
|
+
/* @__PURE__ */ jsxRuntime.jsx(antd.Button, { onClick: onClose, children: "Cancel" }),
|
|
18395
|
+
/* @__PURE__ */ jsxRuntime.jsx(antd.Button, { type: "primary", onClick: handleSave, children: "Save" })
|
|
18396
|
+
] }),
|
|
18397
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(antd.Form, { form, layout: "vertical", size: "small", children: [
|
|
18398
|
+
/* @__PURE__ */ jsxRuntime.jsx(antd.Divider, { orientation: "left", children: "Tab" }),
|
|
18399
|
+
/* @__PURE__ */ jsxRuntime.jsx(antd.Form.Item, { name: "tabName", label: "Tab name", children: /* @__PURE__ */ jsxRuntime.jsx(antd.Input, {}) }),
|
|
18400
|
+
/* @__PURE__ */ jsxRuntime.jsx(antd.Divider, { orientation: "left", children: "Position" }),
|
|
18401
|
+
/* @__PURE__ */ jsxRuntime.jsxs(antd.Space, { children: [
|
|
18402
|
+
/* @__PURE__ */ jsxRuntime.jsx(antd.Form.Item, { name: "row", label: "Row", style: { marginBottom: 0 }, children: /* @__PURE__ */ jsxRuntime.jsx(antd.InputNumber, { min: 1, style: { width: 80 } }) }),
|
|
18403
|
+
/* @__PURE__ */ jsxRuntime.jsx(antd.Form.Item, { name: "col", label: "Column", style: { marginBottom: 0 }, children: /* @__PURE__ */ jsxRuntime.jsx(antd.InputNumber, { min: 1, style: { width: 80 } }) })
|
|
18404
|
+
] }),
|
|
18405
|
+
/* @__PURE__ */ jsxRuntime.jsx(antd.Divider, { orientation: "left", children: "View" }),
|
|
18406
|
+
/* @__PURE__ */ jsxRuntime.jsx(antd.Form.Item, { name: "view_type", label: "View type", children: /* @__PURE__ */ jsxRuntime.jsx(antd.Select, { options: VIEW_TYPE_OPTIONS }) }),
|
|
18407
|
+
/* @__PURE__ */ jsxRuntime.jsx(antd.Divider, { orientation: "left", children: "Size" }),
|
|
18408
|
+
/* @__PURE__ */ jsxRuntime.jsxs(antd.Space, { wrap: true, children: [
|
|
18409
|
+
/* @__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 } }) }),
|
|
18410
|
+
/* @__PURE__ */ jsxRuntime.jsx(antd.Form.Item, { name: "max_width", label: "Max width", style: { marginBottom: 0 }, children: /* @__PURE__ */ jsxRuntime.jsx(antd.Input, { placeholder: "e.g. 800px", style: { width: 130 } }) })
|
|
18411
|
+
] }),
|
|
18412
|
+
/* @__PURE__ */ jsxRuntime.jsxs(antd.Space, { wrap: true, style: { marginTop: 8 }, children: [
|
|
18413
|
+
/* @__PURE__ */ jsxRuntime.jsx(antd.Form.Item, { name: "min_height", label: "Min height", style: { marginBottom: 0 }, children: /* @__PURE__ */ jsxRuntime.jsx(antd.Input, { placeholder: "e.g. 300px", style: { width: 130 } }) }),
|
|
18414
|
+
/* @__PURE__ */ jsxRuntime.jsx(antd.Form.Item, { name: "max_height", label: "Max height", style: { marginBottom: 0 }, children: /* @__PURE__ */ jsxRuntime.jsx(antd.Input, { placeholder: "e.g. 600px", style: { width: 130 } }) })
|
|
18415
|
+
] }),
|
|
18416
|
+
/* @__PURE__ */ jsxRuntime.jsx(antd.Divider, { orientation: "left", children: "Style" }),
|
|
18417
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
18418
|
+
antd.Form.Item,
|
|
18419
|
+
{
|
|
18420
|
+
name: "html_style",
|
|
18421
|
+
label: /* @__PURE__ */ jsxRuntime.jsxs(Text, { children: [
|
|
18422
|
+
"HTML style ",
|
|
18423
|
+
/* @__PURE__ */ jsxRuntime.jsx(Text, { type: "secondary", children: "(inline CSS)" })
|
|
18424
|
+
] }),
|
|
18425
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
18426
|
+
antd.Input.TextArea,
|
|
18427
|
+
{
|
|
18428
|
+
rows: 4,
|
|
18429
|
+
placeholder: "e.g. background-color: #f0f4ff; border-radius: 8px;",
|
|
18430
|
+
style: { fontFamily: "monospace", fontSize: 12 }
|
|
18431
|
+
}
|
|
18432
|
+
)
|
|
18433
|
+
}
|
|
18434
|
+
)
|
|
18435
|
+
] })
|
|
18436
|
+
}
|
|
18437
|
+
);
|
|
18438
|
+
};
|
|
18439
|
+
var DashboardGridCell = ({ cell, allModels, isMaximized, isMinimized, onConfigure, onMaximize, onMinimize }) => {
|
|
18440
|
+
const { token } = antd.theme.useToken();
|
|
18441
|
+
const model = findModelByName(allModels, cell.model);
|
|
18442
|
+
const cellStyle = {
|
|
18443
|
+
border: `1px solid ${token.colorBorderSecondary}`,
|
|
18444
|
+
borderRadius: token.borderRadiusLG,
|
|
18445
|
+
overflow: "hidden",
|
|
18446
|
+
display: "flex",
|
|
18447
|
+
flexDirection: "column",
|
|
18448
|
+
background: token.colorBgContainer,
|
|
18449
|
+
...cell.min_width ? { minWidth: cell.min_width } : {},
|
|
18450
|
+
...cell.max_width ? { maxWidth: cell.max_width } : {},
|
|
18451
|
+
...cell.min_height ? { minHeight: cell.min_height } : {},
|
|
18452
|
+
...cell.max_height ? { maxHeight: cell.max_height } : {},
|
|
18453
|
+
...cell.html_style ? parseInlineStyle3(cell.html_style) : {},
|
|
18454
|
+
...isMaximized ? { gridColumn: "1 / -1" } : {},
|
|
18455
|
+
...isMinimized ? { minHeight: 0 } : {}
|
|
18456
|
+
};
|
|
18457
|
+
const toolbarStyle = {
|
|
18458
|
+
display: "flex",
|
|
18459
|
+
alignItems: "center",
|
|
18460
|
+
justifyContent: "space-between",
|
|
18461
|
+
padding: "2px 8px",
|
|
18462
|
+
gap: 2,
|
|
18463
|
+
borderBottom: `1px solid ${token.colorBorderSecondary}`,
|
|
18464
|
+
background: token.colorBgContainer,
|
|
18465
|
+
flexShrink: 0,
|
|
18466
|
+
minHeight: 32,
|
|
18467
|
+
position: "relative"
|
|
18468
|
+
};
|
|
18469
|
+
const resource = model?.resource || cell.model;
|
|
18470
|
+
const cellTitle = model?.label || cell.model;
|
|
18471
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: cellStyle, className: "jm-dashboard-cell", children: [
|
|
18472
|
+
/* @__PURE__ */ jsxRuntime.jsx("style", { children: `
|
|
18473
|
+
.jm-dashboard-cell .jm-cell-actions { opacity: 0; transition: opacity 0.15s; }
|
|
18474
|
+
.jm-dashboard-cell:hover .jm-cell-actions { opacity: 1; }
|
|
18475
|
+
` }),
|
|
18476
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: toolbarStyle, children: [
|
|
18477
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: {
|
|
18478
|
+
fontSize: token.fontSizeSM,
|
|
18479
|
+
fontWeight: token.fontWeightStrong,
|
|
18480
|
+
color: token.colorText,
|
|
18481
|
+
paddingLeft: 4,
|
|
18482
|
+
overflow: "hidden",
|
|
18483
|
+
textOverflow: "ellipsis",
|
|
18484
|
+
whiteSpace: "nowrap"
|
|
18485
|
+
}, children: cellTitle }),
|
|
18486
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "jm-cell-actions", style: { display: "flex", alignItems: "center", gap: 2 }, children: [
|
|
18487
|
+
/* @__PURE__ */ jsxRuntime.jsx(antd.Tooltip, { title: "Configure cell", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
18488
|
+
antd.Button,
|
|
18489
|
+
{
|
|
18490
|
+
type: "text",
|
|
18491
|
+
size: "small",
|
|
18492
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(icons.SettingOutlined, { style: { fontSize: 11 } }),
|
|
18493
|
+
onClick: onConfigure,
|
|
18494
|
+
style: { color: token.colorTextTertiary, padding: "0 4px", height: 22, minWidth: 22 }
|
|
18495
|
+
}
|
|
18496
|
+
) }),
|
|
18497
|
+
/* @__PURE__ */ jsxRuntime.jsx(antd.Tooltip, { title: "Open full page", children: /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Link, { to: `/${resource}`, style: { color: token.colorTextTertiary, display: "flex", alignItems: "center", padding: "0 4px" }, children: /* @__PURE__ */ jsxRuntime.jsx(icons.LinkOutlined, { style: { fontSize: 11 } }) }) }),
|
|
18498
|
+
/* @__PURE__ */ jsxRuntime.jsx(antd.Tooltip, { title: isMaximized ? "Restore" : "Maximize", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
18499
|
+
antd.Button,
|
|
18500
|
+
{
|
|
18501
|
+
type: "text",
|
|
18502
|
+
size: "small",
|
|
18503
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(icons.FullscreenOutlined, { style: { fontSize: 11 } }),
|
|
18504
|
+
onClick: onMaximize,
|
|
18505
|
+
style: { color: token.colorTextTertiary, padding: "0 4px", height: 22, minWidth: 22 }
|
|
18506
|
+
}
|
|
18507
|
+
) }),
|
|
18508
|
+
/* @__PURE__ */ jsxRuntime.jsx(antd.Tooltip, { title: isMinimized ? "Restore" : "Minimize", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
18509
|
+
antd.Button,
|
|
18510
|
+
{
|
|
18511
|
+
type: "text",
|
|
18512
|
+
size: "small",
|
|
18513
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(icons.MinusSquareOutlined, { style: { fontSize: 11 } }),
|
|
18514
|
+
onClick: onMinimize,
|
|
18515
|
+
style: { color: token.colorTextTertiary, padding: "0 4px", height: 22, minWidth: 22 }
|
|
18516
|
+
}
|
|
18517
|
+
) })
|
|
18518
|
+
] })
|
|
18519
|
+
] }),
|
|
18520
|
+
!isMinimized && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { flex: 1, overflow: "auto", minHeight: 0 }, children: model ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
18521
|
+
DynamicList,
|
|
18522
|
+
{
|
|
18523
|
+
model,
|
|
18524
|
+
allModels,
|
|
18525
|
+
isEmbedded: true,
|
|
18526
|
+
preferencesResourceOverride: `dashboard:${resource}`,
|
|
18527
|
+
defaultListVisible: false,
|
|
18528
|
+
listViewType: cell.view_type ? cell.view_type : model.listViewType
|
|
18529
|
+
}
|
|
18530
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
18531
|
+
antd.Empty,
|
|
18532
|
+
{
|
|
18533
|
+
description: `Model "${cell.model}" not found`,
|
|
18534
|
+
style: { padding: 24 },
|
|
18535
|
+
image: antd.Empty.PRESENTED_IMAGE_SIMPLE
|
|
18536
|
+
}
|
|
18537
|
+
) })
|
|
18538
|
+
] });
|
|
18539
|
+
};
|
|
18540
|
+
var DashboardTabContent = ({ tab, allModels, maximizedCellId, minimizedCellIds, onMaximize, onMinimize, onConfigure }) => {
|
|
18541
|
+
const cells = tab.cells;
|
|
18542
|
+
const numCols = React6.useMemo(() => {
|
|
18543
|
+
if (!cells.length) return 2;
|
|
18544
|
+
return Math.max(...cells.map((c) => c.col)) + 1;
|
|
18545
|
+
}, [cells]);
|
|
18546
|
+
const numRows = React6.useMemo(() => {
|
|
18547
|
+
if (!cells.length) return 1;
|
|
18548
|
+
return Math.max(...cells.map((c) => c.row)) + 1;
|
|
18549
|
+
}, [cells]);
|
|
18550
|
+
const visibleCells = maximizedCellId ? cells.filter((c) => c.id === maximizedCellId) : cells;
|
|
18551
|
+
const gridStyle = {
|
|
18552
|
+
display: "grid",
|
|
18553
|
+
gridTemplateColumns: maximizedCellId ? "1fr" : `repeat(${numCols}, 1fr)`,
|
|
18554
|
+
gridTemplateRows: maximizedCellId ? "1fr" : `repeat(${numRows}, minmax(320px, auto))`,
|
|
18555
|
+
gap: 12,
|
|
18556
|
+
padding: 12,
|
|
18557
|
+
height: "100%",
|
|
18558
|
+
boxSizing: "border-box"
|
|
18559
|
+
};
|
|
18560
|
+
if (!cells.length) {
|
|
18561
|
+
return /* @__PURE__ */ jsxRuntime.jsx(antd.Empty, { description: "No models in this tab", style: { padding: 48 } });
|
|
18562
|
+
}
|
|
18563
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: gridStyle, children: visibleCells.map((cell) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
18564
|
+
"div",
|
|
18565
|
+
{
|
|
18566
|
+
style: {
|
|
18567
|
+
gridColumn: maximizedCellId ? "1 / -1" : `${cell.col + 1}`,
|
|
18568
|
+
gridRow: maximizedCellId ? "1 / -1" : `${cell.row + 1}`
|
|
18569
|
+
},
|
|
18570
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
18571
|
+
DashboardGridCell,
|
|
18572
|
+
{
|
|
18573
|
+
cell,
|
|
18574
|
+
allModels,
|
|
18575
|
+
isMaximized: maximizedCellId === cell.id,
|
|
18576
|
+
isMinimized: minimizedCellIds.has(cell.id),
|
|
18577
|
+
onConfigure: () => onConfigure(cell),
|
|
18578
|
+
onMaximize: () => onMaximize(cell.id),
|
|
18579
|
+
onMinimize: () => onMinimize(cell.id)
|
|
18580
|
+
}
|
|
18581
|
+
)
|
|
18582
|
+
},
|
|
18583
|
+
cell.id
|
|
18584
|
+
)) });
|
|
18585
|
+
};
|
|
18586
|
+
var ViewsGrid = ({ config, allModels, onConfigChange }) => {
|
|
18587
|
+
const [maximizedCellId, setMaximizedCellId] = React6.useState(null);
|
|
18588
|
+
const [minimizedCellIds, setMinimizedCellIds] = React6.useState(/* @__PURE__ */ new Set());
|
|
18589
|
+
const [drawerSelection, setDrawerSelection] = React6.useState(null);
|
|
18590
|
+
const handleMaximize = React6.useCallback((cellId) => {
|
|
18591
|
+
setMaximizedCellId((prev) => prev === cellId ? null : cellId);
|
|
18592
|
+
}, []);
|
|
18593
|
+
const handleMinimize = React6.useCallback((cellId) => {
|
|
18594
|
+
setMinimizedCellIds((prev) => {
|
|
18595
|
+
const next = new Set(prev);
|
|
18596
|
+
if (next.has(cellId)) {
|
|
18597
|
+
next.delete(cellId);
|
|
18598
|
+
} else {
|
|
18599
|
+
next.add(cellId);
|
|
18600
|
+
}
|
|
18601
|
+
return next;
|
|
18602
|
+
});
|
|
18603
|
+
}, []);
|
|
18604
|
+
const handleOpenDrawer = React6.useCallback((tabId, cell) => {
|
|
18605
|
+
setDrawerSelection({ tabId, cell });
|
|
18606
|
+
}, []);
|
|
18607
|
+
const handleSaveConfig = React6.useCallback((nextConfig) => {
|
|
18608
|
+
onConfigChange(nextConfig);
|
|
18609
|
+
setDrawerSelection(null);
|
|
18610
|
+
}, [onConfigChange]);
|
|
18611
|
+
const tabItems = React6.useMemo(
|
|
18612
|
+
() => config.tabs.map((tab) => ({
|
|
18613
|
+
key: tab.id,
|
|
18614
|
+
label: tab.name,
|
|
18615
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
18616
|
+
DashboardTabContent,
|
|
18617
|
+
{
|
|
18618
|
+
tab,
|
|
18619
|
+
allModels,
|
|
18620
|
+
maximizedCellId,
|
|
18621
|
+
minimizedCellIds,
|
|
18622
|
+
onMaximize: handleMaximize,
|
|
18623
|
+
onMinimize: handleMinimize,
|
|
18624
|
+
onConfigure: (cell) => handleOpenDrawer(tab.id, cell)
|
|
18625
|
+
}
|
|
18626
|
+
)
|
|
18627
|
+
})),
|
|
18628
|
+
[config.tabs, allModels, maximizedCellId, minimizedCellIds, handleMaximize, handleMinimize, handleOpenDrawer]
|
|
18629
|
+
);
|
|
18630
|
+
if (!config.tabs.length) {
|
|
18631
|
+
return /* @__PURE__ */ jsxRuntime.jsx(antd.Empty, { description: "No tabs configured. Run veloiq add-dashboard to add models.", style: { padding: 48 } });
|
|
18632
|
+
}
|
|
18633
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
18634
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
18635
|
+
antd.Tabs,
|
|
18636
|
+
{
|
|
18637
|
+
items: tabItems,
|
|
18638
|
+
onChange: () => {
|
|
18639
|
+
setMaximizedCellId(null);
|
|
18640
|
+
setMinimizedCellIds(/* @__PURE__ */ new Set());
|
|
18641
|
+
},
|
|
18642
|
+
style: { height: "100%" },
|
|
18643
|
+
tabBarStyle: { paddingLeft: 12, marginBottom: 0 }
|
|
18644
|
+
}
|
|
18645
|
+
),
|
|
18646
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
18647
|
+
CellConfigDrawer,
|
|
18648
|
+
{
|
|
18649
|
+
open: Boolean(drawerSelection),
|
|
18650
|
+
cell: drawerSelection?.cell ?? null,
|
|
18651
|
+
tabId: drawerSelection?.tabId ?? null,
|
|
18652
|
+
config,
|
|
18653
|
+
onClose: () => setDrawerSelection(null),
|
|
18654
|
+
onSave: handleSaveConfig
|
|
18655
|
+
}
|
|
18656
|
+
)
|
|
18657
|
+
] });
|
|
18658
|
+
};
|
|
18659
|
+
function parseInlineStyle3(cssText) {
|
|
18660
|
+
const result = {};
|
|
18661
|
+
cssText.split(";").forEach((declaration) => {
|
|
18662
|
+
const idx = declaration.indexOf(":");
|
|
18663
|
+
if (idx < 0) return;
|
|
18664
|
+
const prop = declaration.slice(0, idx).trim();
|
|
18665
|
+
const value = declaration.slice(idx + 1).trim();
|
|
18666
|
+
if (!prop || !value) return;
|
|
18667
|
+
const camel = prop.replace(/-([a-z])/g, (_39, c) => c.toUpperCase());
|
|
18668
|
+
result[camel] = value;
|
|
18669
|
+
});
|
|
18670
|
+
return result;
|
|
18671
|
+
}
|
|
18672
|
+
function useRecentActivity(days) {
|
|
18673
|
+
const [data, setData] = React6.useState(null);
|
|
18674
|
+
const [loading, setLoading] = React6.useState(true);
|
|
18675
|
+
const load = React6.useCallback(async () => {
|
|
18676
|
+
setLoading(true);
|
|
18677
|
+
try {
|
|
18678
|
+
const params = days !== void 0 ? `?days=${days}` : "";
|
|
18679
|
+
const res = await authenticatedFetch(`${API_URL3}/dashboard/recent-activity${params}`);
|
|
18680
|
+
if (res.ok) setData(await res.json());
|
|
18681
|
+
} catch {
|
|
18682
|
+
} finally {
|
|
18683
|
+
setLoading(false);
|
|
18684
|
+
}
|
|
18685
|
+
}, [days]);
|
|
18686
|
+
React6.useEffect(() => {
|
|
18687
|
+
load();
|
|
18688
|
+
}, [load]);
|
|
18689
|
+
return { data, loading, reload: load };
|
|
18690
|
+
}
|
|
18691
|
+
var { Text: Text2, Title: Title9 } = antd.Typography;
|
|
18692
|
+
function relativeTime(iso) {
|
|
18693
|
+
if (!iso) return "";
|
|
18694
|
+
const diff = Date.now() - new Date(iso).getTime();
|
|
18695
|
+
const mins = Math.floor(diff / 6e4);
|
|
18696
|
+
if (mins < 1) return "just now";
|
|
18697
|
+
if (mins < 60) return `${mins}m ago`;
|
|
18698
|
+
const hrs = Math.floor(mins / 60);
|
|
18699
|
+
if (hrs < 24) return `${hrs}h ago`;
|
|
18700
|
+
const days = Math.floor(hrs / 24);
|
|
18701
|
+
if (days < 30) return `${days}d ago`;
|
|
18702
|
+
return new Date(iso).toLocaleDateString();
|
|
18703
|
+
}
|
|
18704
|
+
var RecentActivityPanel = () => {
|
|
18705
|
+
const { token } = antd.theme.useToken();
|
|
18706
|
+
const allModels = useAllModels();
|
|
18707
|
+
const [days, setDays] = React6.useState(30);
|
|
18708
|
+
const { data, loading, reload } = useRecentActivity(days);
|
|
18709
|
+
const groups = data?.groups ?? [];
|
|
18710
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { padding: "16px 0" }, children: [
|
|
18711
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 12, marginBottom: 20, paddingLeft: 4 }, children: [
|
|
18712
|
+
/* @__PURE__ */ jsxRuntime.jsx(Text2, { type: "secondary", children: "Show activity from the last" }),
|
|
18713
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
18714
|
+
antd.InputNumber,
|
|
18715
|
+
{
|
|
18716
|
+
min: 1,
|
|
18717
|
+
max: 365,
|
|
18718
|
+
value: days,
|
|
18719
|
+
onChange: (v) => v && setDays(v),
|
|
18720
|
+
style: { width: 72 },
|
|
18721
|
+
size: "small"
|
|
18722
|
+
}
|
|
18723
|
+
),
|
|
18724
|
+
/* @__PURE__ */ jsxRuntime.jsx(Text2, { type: "secondary", children: "days" }),
|
|
18725
|
+
/* @__PURE__ */ jsxRuntime.jsx(antd.Tooltip, { title: "Refresh", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
18726
|
+
icons.ReloadOutlined,
|
|
18727
|
+
{
|
|
18728
|
+
style: { color: token.colorTextTertiary, cursor: "pointer", fontSize: 13 },
|
|
18729
|
+
onClick: reload
|
|
18730
|
+
}
|
|
18731
|
+
) }),
|
|
18732
|
+
data && /* @__PURE__ */ jsxRuntime.jsxs(Text2, { type: "secondary", style: { fontSize: 12 }, children: [
|
|
18733
|
+
groups.reduce((n, g) => n + g.records.length, 0),
|
|
18734
|
+
" records across ",
|
|
18735
|
+
groups.length,
|
|
18736
|
+
" models"
|
|
18737
|
+
] })
|
|
18738
|
+
] }),
|
|
18739
|
+
loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", justifyContent: "center", padding: 48 }, children: /* @__PURE__ */ jsxRuntime.jsx(antd.Spin, {}) }) : groups.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
18740
|
+
antd.Empty,
|
|
18741
|
+
{
|
|
18742
|
+
description: `No activity in the last ${days} days`,
|
|
18743
|
+
image: antd.Empty.PRESENTED_IMAGE_SIMPLE,
|
|
18744
|
+
style: { padding: 48 }
|
|
18745
|
+
}
|
|
18746
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(antd.Space, { direction: "vertical", size: 24, style: { width: "100%" }, children: groups.map((group) => {
|
|
18747
|
+
const model = findModelByName(allModels, group.resource);
|
|
18748
|
+
const tone = getModelTone(model?.name ?? group.resource);
|
|
18749
|
+
const label = model?.label ?? group.model_name;
|
|
18750
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
18751
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: {
|
|
18752
|
+
display: "flex",
|
|
18753
|
+
alignItems: "center",
|
|
18754
|
+
gap: 8,
|
|
18755
|
+
marginBottom: 6,
|
|
18756
|
+
paddingBottom: 6,
|
|
18757
|
+
borderBottom: `2px solid ${tone.solid}40`
|
|
18758
|
+
}, children: [
|
|
18759
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: {
|
|
18760
|
+
width: 10,
|
|
18761
|
+
height: 10,
|
|
18762
|
+
borderRadius: "50%",
|
|
18763
|
+
background: tone.solid,
|
|
18764
|
+
flexShrink: 0
|
|
18765
|
+
} }),
|
|
18766
|
+
/* @__PURE__ */ jsxRuntime.jsx(Title9, { level: 5, style: { margin: 0, color: tone.text }, children: label }),
|
|
18767
|
+
/* @__PURE__ */ jsxRuntime.jsx(antd.Tag, { color: tone.solid, style: { marginLeft: "auto", fontSize: 11 }, children: group.records.length })
|
|
18768
|
+
] }),
|
|
18769
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
18770
|
+
antd.List,
|
|
18771
|
+
{
|
|
18772
|
+
size: "small",
|
|
18773
|
+
dataSource: group.records,
|
|
18774
|
+
renderItem: (rec) => {
|
|
18775
|
+
const timestamp = rec.updated_at || rec.created_at;
|
|
18776
|
+
const isNew = rec.created_at === rec.updated_at;
|
|
18777
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
18778
|
+
antd.List.Item,
|
|
18779
|
+
{
|
|
18780
|
+
style: {
|
|
18781
|
+
padding: "4px 8px",
|
|
18782
|
+
borderRadius: token.borderRadius,
|
|
18783
|
+
transition: "background 0.15s"
|
|
18784
|
+
},
|
|
18785
|
+
className: "jm-activity-row",
|
|
18786
|
+
children: [
|
|
18787
|
+
/* @__PURE__ */ jsxRuntime.jsx("style", { children: `
|
|
18788
|
+
.jm-activity-row:hover { background: ${token.colorFillAlter}; }
|
|
18789
|
+
` }),
|
|
18790
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8, width: "100%" }, children: [
|
|
18791
|
+
/* @__PURE__ */ jsxRuntime.jsx(icons.ClockCircleOutlined, { style: { color: token.colorTextTertiary, fontSize: 11, flexShrink: 0 } }),
|
|
18792
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
18793
|
+
reactRouterDom.Link,
|
|
18794
|
+
{
|
|
18795
|
+
to: `/${group.resource}/show/${rec.id}`,
|
|
18796
|
+
style: { flex: 1, color: token.colorText, fontWeight: 500, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
|
|
18797
|
+
children: rec._label || `#${rec.id}`
|
|
18798
|
+
}
|
|
18799
|
+
),
|
|
18800
|
+
isNew && /* @__PURE__ */ jsxRuntime.jsx(antd.Tag, { color: "green", style: { fontSize: 10, padding: "0 4px", lineHeight: "16px" }, children: "new" }),
|
|
18801
|
+
/* @__PURE__ */ jsxRuntime.jsx(Text2, { type: "secondary", style: { fontSize: 11, flexShrink: 0 }, children: relativeTime(timestamp) })
|
|
18802
|
+
] })
|
|
18803
|
+
]
|
|
18804
|
+
}
|
|
18805
|
+
);
|
|
18806
|
+
}
|
|
18807
|
+
}
|
|
18808
|
+
)
|
|
18809
|
+
] }, group.resource);
|
|
18810
|
+
}) })
|
|
18811
|
+
] });
|
|
18812
|
+
};
|
|
18813
|
+
var { Text: AntText, Title: AntTitle } = antd.Typography;
|
|
18814
|
+
function usePinnedRecords() {
|
|
18815
|
+
const [groups, setGroups] = React6.useState([]);
|
|
18816
|
+
const [loading, setLoading] = React6.useState(true);
|
|
18817
|
+
const load = React6.useCallback(async () => {
|
|
18818
|
+
setLoading(true);
|
|
18819
|
+
try {
|
|
18820
|
+
const res = await authenticatedFetch(`${API_URL3}/dashboard/pinned-records`);
|
|
18821
|
+
if (res.ok) {
|
|
18822
|
+
const data = await res.json();
|
|
18823
|
+
setGroups(data.groups ?? []);
|
|
18824
|
+
}
|
|
18825
|
+
} catch {
|
|
18826
|
+
} finally {
|
|
18827
|
+
setLoading(false);
|
|
18828
|
+
}
|
|
18829
|
+
}, []);
|
|
18830
|
+
React6__default.default.useEffect(() => {
|
|
18831
|
+
load();
|
|
18832
|
+
}, [load]);
|
|
18833
|
+
return { groups, loading, reload: load };
|
|
18834
|
+
}
|
|
18835
|
+
var PinnedRecordsPanel = () => {
|
|
18836
|
+
const { token } = antd.theme.useToken();
|
|
18837
|
+
const allModels = useAllModels();
|
|
18838
|
+
const { groups, loading, reload } = usePinnedRecords();
|
|
18839
|
+
const [unpinning, setUnpinning] = React6.useState(/* @__PURE__ */ new Set());
|
|
18840
|
+
const visibleGroups = groups.filter((g) => findModelByName(allModels, g.resource));
|
|
18841
|
+
const handleUnpin = React6.useCallback(async (resource, recordId) => {
|
|
18842
|
+
const key = `${resource}:${recordId}`;
|
|
18843
|
+
setUnpinning((prev) => new Set(prev).add(key));
|
|
18844
|
+
try {
|
|
18845
|
+
await unpinRecords(resource, [recordId]);
|
|
18846
|
+
await reload();
|
|
18847
|
+
} finally {
|
|
18848
|
+
setUnpinning((prev) => {
|
|
18849
|
+
const next = new Set(prev);
|
|
18850
|
+
next.delete(key);
|
|
18851
|
+
return next;
|
|
18852
|
+
});
|
|
18853
|
+
}
|
|
18854
|
+
}, [reload]);
|
|
18855
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { padding: "16px 0" }, children: [
|
|
18856
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 12, marginBottom: 20, paddingLeft: 4 }, children: [
|
|
18857
|
+
/* @__PURE__ */ jsxRuntime.jsx(icons.PushpinFilled, { style: { color: "#faad14", fontSize: 14 } }),
|
|
18858
|
+
/* @__PURE__ */ jsxRuntime.jsx(AntText, { type: "secondary", children: "Records you've pinned across the app" }),
|
|
18859
|
+
/* @__PURE__ */ jsxRuntime.jsx(antd.Tooltip, { title: "Refresh", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
18860
|
+
icons.ReloadOutlined,
|
|
18861
|
+
{
|
|
18862
|
+
style: { color: token.colorTextTertiary, cursor: "pointer", fontSize: 13 },
|
|
18863
|
+
onClick: reload
|
|
18864
|
+
}
|
|
18865
|
+
) }),
|
|
18866
|
+
!loading && /* @__PURE__ */ jsxRuntime.jsxs(AntText, { type: "secondary", style: { fontSize: 12 }, children: [
|
|
18867
|
+
visibleGroups.reduce((n, g) => n + g.records.length, 0),
|
|
18868
|
+
" pins across ",
|
|
18869
|
+
visibleGroups.length,
|
|
18870
|
+
" models"
|
|
18871
|
+
] })
|
|
18872
|
+
] }),
|
|
18873
|
+
loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", justifyContent: "center", padding: 48 }, children: /* @__PURE__ */ jsxRuntime.jsx(antd.Spin, {}) }) : visibleGroups.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
18874
|
+
antd.Empty,
|
|
18875
|
+
{
|
|
18876
|
+
description: /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
18877
|
+
"No pinned records yet.",
|
|
18878
|
+
/* @__PURE__ */ jsxRuntime.jsx("br", {}),
|
|
18879
|
+
/* @__PURE__ */ jsxRuntime.jsxs(AntText, { type: "secondary", style: { fontSize: 12 }, children: [
|
|
18880
|
+
"Open any record and click the ",
|
|
18881
|
+
/* @__PURE__ */ jsxRuntime.jsx(icons.PushpinOutlined, {}),
|
|
18882
|
+
" pin button to pin it here."
|
|
18883
|
+
] })
|
|
18884
|
+
] }),
|
|
18885
|
+
image: antd.Empty.PRESENTED_IMAGE_SIMPLE,
|
|
18886
|
+
style: { padding: 48 }
|
|
18887
|
+
}
|
|
18888
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(antd.Space, { direction: "vertical", size: 24, style: { width: "100%" }, children: visibleGroups.map((group) => {
|
|
18889
|
+
const model = findModelByName(allModels, group.resource);
|
|
18890
|
+
const tone = getModelTone(model?.name ?? group.resource);
|
|
18891
|
+
const label = model?.label ?? group.model_name;
|
|
18892
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
18893
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: {
|
|
18894
|
+
display: "flex",
|
|
18895
|
+
alignItems: "center",
|
|
18896
|
+
gap: 8,
|
|
18897
|
+
marginBottom: 6,
|
|
18898
|
+
paddingBottom: 6,
|
|
18899
|
+
borderBottom: `2px solid ${tone.solid}40`
|
|
18900
|
+
}, children: [
|
|
18901
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: {
|
|
18902
|
+
width: 10,
|
|
18903
|
+
height: 10,
|
|
18904
|
+
borderRadius: "50%",
|
|
18905
|
+
background: tone.solid,
|
|
18906
|
+
flexShrink: 0
|
|
18907
|
+
} }),
|
|
18908
|
+
/* @__PURE__ */ jsxRuntime.jsx(AntTitle, { level: 5, style: { margin: 0, color: tone.text }, children: label }),
|
|
18909
|
+
/* @__PURE__ */ jsxRuntime.jsx(antd.Tag, { color: tone.solid, style: { marginLeft: "auto", fontSize: 11 }, children: group.records.length })
|
|
18910
|
+
] }),
|
|
18911
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
18912
|
+
antd.List,
|
|
18913
|
+
{
|
|
18914
|
+
size: "small",
|
|
18915
|
+
dataSource: group.records,
|
|
18916
|
+
renderItem: (rec) => {
|
|
18917
|
+
const key = `${group.resource}:${rec.id}`;
|
|
18918
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
18919
|
+
antd.List.Item,
|
|
18920
|
+
{
|
|
18921
|
+
style: {
|
|
18922
|
+
padding: "4px 8px",
|
|
18923
|
+
borderRadius: token.borderRadius,
|
|
18924
|
+
transition: "background 0.15s"
|
|
18925
|
+
},
|
|
18926
|
+
className: "jm-pin-row",
|
|
18927
|
+
children: [
|
|
18928
|
+
/* @__PURE__ */ jsxRuntime.jsx("style", { children: `
|
|
18929
|
+
.jm-pin-row:hover { background: ${token.colorFillAlter}; }
|
|
18930
|
+
.jm-pin-row .jm-unpin-btn { opacity: 0; transition: opacity 0.15s; }
|
|
18931
|
+
.jm-pin-row:hover .jm-unpin-btn { opacity: 1; }
|
|
18932
|
+
` }),
|
|
18933
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8, width: "100%" }, children: [
|
|
18934
|
+
/* @__PURE__ */ jsxRuntime.jsx(icons.PushpinFilled, { style: { color: "#faad14", fontSize: 11, flexShrink: 0 } }),
|
|
18935
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
18936
|
+
reactRouterDom.Link,
|
|
18937
|
+
{
|
|
18938
|
+
to: `/${group.resource}/show/${rec.id}`,
|
|
18939
|
+
style: { flex: 1, color: token.colorText, fontWeight: 500, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
|
|
18940
|
+
children: rec._label || `#${rec.id}`
|
|
18941
|
+
}
|
|
18942
|
+
),
|
|
18943
|
+
/* @__PURE__ */ jsxRuntime.jsx(antd.Tooltip, { title: "Unpin", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
18944
|
+
antd.Button,
|
|
18945
|
+
{
|
|
18946
|
+
className: "jm-unpin-btn",
|
|
18947
|
+
type: "text",
|
|
18948
|
+
size: "small",
|
|
18949
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(icons.PushpinFilled, { style: { color: "#faad14" } }),
|
|
18950
|
+
loading: unpinning.has(key),
|
|
18951
|
+
onClick: () => handleUnpin(group.resource, rec.id),
|
|
18952
|
+
style: { color: token.colorTextTertiary, height: 20, minWidth: 20, padding: "0 4px" }
|
|
18953
|
+
}
|
|
18954
|
+
) })
|
|
18955
|
+
] })
|
|
18956
|
+
]
|
|
18957
|
+
}
|
|
18958
|
+
);
|
|
18959
|
+
}
|
|
18960
|
+
}
|
|
18961
|
+
)
|
|
18962
|
+
] }, group.resource);
|
|
18963
|
+
}) })
|
|
18964
|
+
] });
|
|
18965
|
+
};
|
|
18966
|
+
var { Text: Text3 } = antd.Typography;
|
|
18967
|
+
var _38 = window._ || ((text) => text);
|
|
18968
|
+
var DashboardPage = () => {
|
|
18969
|
+
const { token } = antd.theme.useToken();
|
|
18970
|
+
const allModels = useAllModels();
|
|
18971
|
+
const { config, enabled, loading, save } = useDashboardConfig();
|
|
18972
|
+
if (loading) {
|
|
18973
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", justifyContent: "center", padding: 64 }, children: /* @__PURE__ */ jsxRuntime.jsx(antd.Spin, {}) });
|
|
18974
|
+
}
|
|
18975
|
+
if (!enabled || !config) {
|
|
18976
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: 48 }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
18977
|
+
antd.Empty,
|
|
18978
|
+
{
|
|
18979
|
+
image: /* @__PURE__ */ jsxRuntime.jsx(icons.DashboardOutlined, { style: { fontSize: 48, color: token.colorTextTertiary } }),
|
|
18980
|
+
imageStyle: { height: 60 },
|
|
18981
|
+
description: /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
18982
|
+
"No dashboard configured.",
|
|
18983
|
+
/* @__PURE__ */ jsxRuntime.jsx("br", {}),
|
|
18984
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Text3, { type: "secondary", children: [
|
|
18985
|
+
"Run ",
|
|
18986
|
+
/* @__PURE__ */ jsxRuntime.jsx("code", { children: "veloiq add-dashboard <model> \u2026" }),
|
|
18987
|
+
" to get started."
|
|
18988
|
+
] })
|
|
18989
|
+
] })
|
|
18990
|
+
}
|
|
18991
|
+
) });
|
|
18992
|
+
}
|
|
18993
|
+
const tabs = [
|
|
18994
|
+
{
|
|
18995
|
+
key: "models_grid",
|
|
18996
|
+
label: _38("Models Grid"),
|
|
18997
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { height: "calc(100vh - 140px)", overflow: "auto" }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
18998
|
+
ViewsGrid,
|
|
18999
|
+
{
|
|
19000
|
+
config,
|
|
19001
|
+
allModels,
|
|
19002
|
+
onConfigChange: save
|
|
19003
|
+
}
|
|
19004
|
+
) })
|
|
19005
|
+
},
|
|
19006
|
+
{
|
|
19007
|
+
key: "recent_activity",
|
|
19008
|
+
label: _38("Recent Activity"),
|
|
19009
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { height: "calc(100vh - 140px)", overflow: "auto", padding: "0 12px" }, children: /* @__PURE__ */ jsxRuntime.jsx(RecentActivityPanel, {}) })
|
|
19010
|
+
},
|
|
19011
|
+
{
|
|
19012
|
+
key: "pinned_records",
|
|
19013
|
+
label: _38("Pinned Records"),
|
|
19014
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { height: "calc(100vh - 140px)", overflow: "auto", padding: "0 12px" }, children: /* @__PURE__ */ jsxRuntime.jsx(PinnedRecordsPanel, {}) })
|
|
19015
|
+
}
|
|
19016
|
+
];
|
|
19017
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "0 16px", height: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
19018
|
+
antd.Tabs,
|
|
19019
|
+
{
|
|
19020
|
+
items: tabs,
|
|
19021
|
+
tabBarStyle: { marginBottom: 0 }
|
|
19022
|
+
}
|
|
19023
|
+
) });
|
|
19024
|
+
};
|
|
18173
19025
|
|
|
18174
19026
|
// src/utils/generateResources.ts
|
|
18175
19027
|
function generateResources(models, moduleName, options = {}) {
|
|
@@ -18318,6 +19170,7 @@ exports.AllModelsProvider = AllModelsProvider;
|
|
|
18318
19170
|
exports.ColorModeContext = ColorModeContext;
|
|
18319
19171
|
exports.ColorModeContextProvider = ColorModeContextProvider;
|
|
18320
19172
|
exports.CustomSider = CustomSider;
|
|
19173
|
+
exports.DashboardPage = DashboardPage;
|
|
18321
19174
|
exports.DynamicCreate = DynamicCreate;
|
|
18322
19175
|
exports.DynamicEdit = DynamicEdit;
|
|
18323
19176
|
exports.DynamicList = DynamicList;
|
|
@@ -18332,12 +19185,15 @@ exports.LoginPage = LoginPage;
|
|
|
18332
19185
|
exports.ModelHeading = ModelHeading;
|
|
18333
19186
|
exports.MultiPaneLayout = MultiPaneLayout;
|
|
18334
19187
|
exports.PaneNavigationContext = PaneNavigationContext;
|
|
19188
|
+
exports.PinnedRecordsPanel = PinnedRecordsPanel;
|
|
18335
19189
|
exports.PrimaryShowContext = PrimaryShowContext;
|
|
19190
|
+
exports.RecentActivityPanel = RecentActivityPanel;
|
|
18336
19191
|
exports.ReferenceField = ReferenceField;
|
|
18337
19192
|
exports.ResourceContext = ResourceContext;
|
|
18338
19193
|
exports.ShowFooterButtons = ShowFooterButtons;
|
|
18339
19194
|
exports.StandardList = StandardList;
|
|
18340
19195
|
exports.StandardShow = StandardShow;
|
|
19196
|
+
exports.ViewsGrid = ViewsGrid;
|
|
18341
19197
|
exports.accessControlProvider = accessControlProvider;
|
|
18342
19198
|
exports.authProvider = authProvider;
|
|
18343
19199
|
exports.authSystemModels = authSystemModels;
|