@evergis/react 3.1.67 → 3.1.68
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/components/Dashboard/containers/TaskContainer/components/LogTerminal/index.d.ts +6 -0
- package/dist/components/Dashboard/containers/TaskContainer/components/LogTerminal/styled.d.ts +1 -0
- package/dist/hooks/task/usePythonTask.d.ts +1 -1
- package/dist/index.js +148 -33
- package/dist/index.js.map +1 -1
- package/dist/react.esm.js +148 -33
- package/dist/react.esm.js.map +1 -1
- package/package.json +4 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const TerminalWrapper: import('styled-components').StyledComponent<"div", any, {}, never>;
|
package/dist/index.js
CHANGED
|
@@ -24,6 +24,9 @@ require('mapbox-gl/dist/mapbox-gl.css');
|
|
|
24
24
|
var react = require('swiper/react');
|
|
25
25
|
var ReactMarkdown = require('react-markdown');
|
|
26
26
|
var remarkGfm = require('remark-gfm');
|
|
27
|
+
var xterm = require('@xterm/xterm');
|
|
28
|
+
var addonFit = require('@xterm/addon-fit');
|
|
29
|
+
require('@xterm/xterm/css/xterm.css');
|
|
27
30
|
|
|
28
31
|
const AddFeatureButton = ({ title, icon = "feature_add" /* , layerName, geometryType*/ }) => {
|
|
29
32
|
// const [, handleAddFeature] = useFeatureCreator(layerName, geometryType);
|
|
@@ -4828,14 +4831,14 @@ const usePythonTask = () => {
|
|
|
4828
4831
|
const { api: api$1, t } = useGlobalContext();
|
|
4829
4832
|
const { addSubscription, connection, unsubscribeById } = useServerNotificationsContext();
|
|
4830
4833
|
const [status, setStatus] = React.useState(api.RemoteTaskStatus.Unknown);
|
|
4831
|
-
const [
|
|
4834
|
+
const [log, setLog] = React.useState(null);
|
|
4832
4835
|
const [error, setError] = React.useState(null);
|
|
4833
4836
|
const [loading, setLoading] = React.useState(false);
|
|
4834
4837
|
const [executionTime, setExecutionTime] = React.useState(null);
|
|
4835
4838
|
const [taskId, setTaskId] = React.useState(null);
|
|
4836
4839
|
const [subscriptionId, setSubscriptionId] = React.useState(null);
|
|
4837
4840
|
const [isLogDialogOpen, setIsLogDialogOpen] = React.useState(false);
|
|
4838
|
-
const
|
|
4841
|
+
const logRef = React.useRef(null);
|
|
4839
4842
|
const reset = React.useCallback(() => {
|
|
4840
4843
|
setStatus(api.RemoteTaskStatus.Unknown);
|
|
4841
4844
|
setError(null);
|
|
@@ -4847,7 +4850,7 @@ const usePythonTask = () => {
|
|
|
4847
4850
|
const runTask = React.useCallback(async ({ resourceId, parameters, script, fileName, methodName, }) => {
|
|
4848
4851
|
reset();
|
|
4849
4852
|
setStatus(api.RemoteTaskStatus.Process);
|
|
4850
|
-
|
|
4853
|
+
setLog(null);
|
|
4851
4854
|
setLoading(true);
|
|
4852
4855
|
const start = Date.now();
|
|
4853
4856
|
let prototypeId = await api$1.remoteTaskManager.createTaskPrototype({
|
|
@@ -4872,7 +4875,7 @@ const usePythonTask = () => {
|
|
|
4872
4875
|
const { id: newTaskId, success } = await api$1.remoteTaskManager.startTask1(prototypeId);
|
|
4873
4876
|
if (!success) {
|
|
4874
4877
|
setStatus(api.RemoteTaskStatus.Error);
|
|
4875
|
-
|
|
4878
|
+
setLog(t("taskRunFail", { ns: "devMode", defaultValue: "Не удалось запустить задачу" }));
|
|
4876
4879
|
setExecutionTime(Date.now() - start);
|
|
4877
4880
|
setLoading(false);
|
|
4878
4881
|
}
|
|
@@ -4885,10 +4888,8 @@ const usePythonTask = () => {
|
|
|
4885
4888
|
const onNotification = ({ data }) => {
|
|
4886
4889
|
if (data?.taskId === newTaskId) {
|
|
4887
4890
|
setStatus(data.status);
|
|
4888
|
-
|
|
4889
|
-
setError(null);
|
|
4891
|
+
setLog([logRef.current, data.log].filter(Boolean).join("\n\n"));
|
|
4890
4892
|
setExecutionTime(Date.now() - start);
|
|
4891
|
-
setLoading(false);
|
|
4892
4893
|
setTaskId([api.RemoteTaskStatus.Completed, api.RemoteTaskStatus.Error].includes(data.status)
|
|
4893
4894
|
? undefined
|
|
4894
4895
|
: newTaskId);
|
|
@@ -4915,14 +4916,14 @@ const usePythonTask = () => {
|
|
|
4915
4916
|
setIsLogDialogOpen(false);
|
|
4916
4917
|
}, []);
|
|
4917
4918
|
React.useEffect(() => {
|
|
4918
|
-
|
|
4919
|
-
}, [
|
|
4919
|
+
logRef.current = log;
|
|
4920
|
+
}, [log]);
|
|
4920
4921
|
return {
|
|
4921
4922
|
taskId,
|
|
4922
4923
|
runTask,
|
|
4923
4924
|
stopTask,
|
|
4924
4925
|
openLog,
|
|
4925
|
-
|
|
4926
|
+
log,
|
|
4926
4927
|
error,
|
|
4927
4928
|
status,
|
|
4928
4929
|
loading,
|
|
@@ -6768,7 +6769,7 @@ const StatusBadge = styled(uilibGl.Flex) `
|
|
|
6768
6769
|
color: ${({ theme }) => theme.palette.iconContrast};
|
|
6769
6770
|
border-radius: 0.25rem;
|
|
6770
6771
|
`;
|
|
6771
|
-
|
|
6772
|
+
styled.div `
|
|
6772
6773
|
flex: 1;
|
|
6773
6774
|
padding: 1rem;
|
|
6774
6775
|
background-color: ${({ theme }) => theme.palette.element};
|
|
@@ -6782,14 +6783,127 @@ const LogContainer = styled.div `
|
|
|
6782
6783
|
max-height: 31.25rem;
|
|
6783
6784
|
`;
|
|
6784
6785
|
|
|
6786
|
+
const TerminalWrapper = styled.div `
|
|
6787
|
+
flex: 1;
|
|
6788
|
+
overflow: hidden;
|
|
6789
|
+
padding: 0;
|
|
6790
|
+
margin: 1rem;
|
|
6791
|
+
box-sizing: border-box;
|
|
6792
|
+
min-height: 0;
|
|
6793
|
+
|
|
6794
|
+
& && .xterm-viewport {
|
|
6795
|
+
overflow-y: auto;
|
|
6796
|
+
}
|
|
6797
|
+
`;
|
|
6798
|
+
|
|
6799
|
+
const LogTerminal = ({ log }) => {
|
|
6800
|
+
const terminalRef = React.useRef(null);
|
|
6801
|
+
const xtermRef = React.useRef(null);
|
|
6802
|
+
const fitAddonRef = React.useRef(null);
|
|
6803
|
+
const previousLogRef = React.useRef("");
|
|
6804
|
+
const theme = styled.useTheme();
|
|
6805
|
+
// Initialize terminal
|
|
6806
|
+
React.useEffect(() => {
|
|
6807
|
+
if (!terminalRef.current)
|
|
6808
|
+
return;
|
|
6809
|
+
// Create terminal instance
|
|
6810
|
+
const terminal = new xterm.Terminal({
|
|
6811
|
+
cursorBlink: false,
|
|
6812
|
+
fontSize: 12,
|
|
6813
|
+
fontFamily: '"Monaco", "Menlo", "Ubuntu Mono", "Consolas", "source-code-pro", monospace',
|
|
6814
|
+
theme: {
|
|
6815
|
+
background: theme.palette.devBackgroundDark,
|
|
6816
|
+
foreground: theme.palette.textPrimary,
|
|
6817
|
+
cursor: theme.palette.primary,
|
|
6818
|
+
},
|
|
6819
|
+
scrollback: 10000,
|
|
6820
|
+
convertEol: true,
|
|
6821
|
+
lineHeight: 1.5,
|
|
6822
|
+
});
|
|
6823
|
+
// Create fit addon
|
|
6824
|
+
const fitAddon = new addonFit.FitAddon();
|
|
6825
|
+
terminal.loadAddon(fitAddon);
|
|
6826
|
+
// Open terminal
|
|
6827
|
+
terminal.open(terminalRef.current);
|
|
6828
|
+
fitAddon.fit();
|
|
6829
|
+
// Store refs
|
|
6830
|
+
xtermRef.current = terminal;
|
|
6831
|
+
fitAddonRef.current = fitAddon;
|
|
6832
|
+
// Handle window resize
|
|
6833
|
+
const handleResize = () => {
|
|
6834
|
+
fitAddon.fit();
|
|
6835
|
+
};
|
|
6836
|
+
window.addEventListener("resize", handleResize);
|
|
6837
|
+
// Cleanup
|
|
6838
|
+
return () => {
|
|
6839
|
+
window.removeEventListener("resize", handleResize);
|
|
6840
|
+
terminal.dispose();
|
|
6841
|
+
xtermRef.current = null;
|
|
6842
|
+
fitAddonRef.current = null;
|
|
6843
|
+
};
|
|
6844
|
+
}, [theme]);
|
|
6845
|
+
// Update log content
|
|
6846
|
+
React.useEffect(() => {
|
|
6847
|
+
if (!xtermRef.current)
|
|
6848
|
+
return;
|
|
6849
|
+
// Handle different log types
|
|
6850
|
+
if (typeof log === "string") {
|
|
6851
|
+
// For string logs, only write the new content (append mode)
|
|
6852
|
+
const previousLog = previousLogRef.current;
|
|
6853
|
+
if (log !== previousLog) {
|
|
6854
|
+
if (log.startsWith(previousLog)) {
|
|
6855
|
+
// Log is accumulated - write only the new part
|
|
6856
|
+
const newContent = log.substring(previousLog.length);
|
|
6857
|
+
xtermRef.current.write(`${newContent.replace(/\n/g, "\r\n")}\r\n`);
|
|
6858
|
+
}
|
|
6859
|
+
else {
|
|
6860
|
+
// Log was replaced completely - clear and write all
|
|
6861
|
+
xtermRef.current.clear();
|
|
6862
|
+
xtermRef.current.write(`${log.replace(/\n/g, "\r\n")}\r\n`);
|
|
6863
|
+
}
|
|
6864
|
+
previousLogRef.current = log;
|
|
6865
|
+
}
|
|
6866
|
+
}
|
|
6867
|
+
else if (typeof log === "object") {
|
|
6868
|
+
// JSON object (results) - always replace
|
|
6869
|
+
xtermRef.current.clear();
|
|
6870
|
+
const formatted = JSON.stringify(log, null, 2);
|
|
6871
|
+
xtermRef.current.write(formatted.replace(/\n/g, "\r\n"));
|
|
6872
|
+
previousLogRef.current = "";
|
|
6873
|
+
}
|
|
6874
|
+
else if (!log) {
|
|
6875
|
+
// No log - clear terminal
|
|
6876
|
+
xtermRef.current.clear();
|
|
6877
|
+
previousLogRef.current = "";
|
|
6878
|
+
}
|
|
6879
|
+
// Scroll to bottom
|
|
6880
|
+
xtermRef.current.scrollToBottom();
|
|
6881
|
+
}, [log]);
|
|
6882
|
+
// Fit terminal on container resize
|
|
6883
|
+
React.useEffect(() => {
|
|
6884
|
+
if (!fitAddonRef.current)
|
|
6885
|
+
return;
|
|
6886
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
6887
|
+
fitAddonRef.current?.fit();
|
|
6888
|
+
});
|
|
6889
|
+
if (terminalRef.current) {
|
|
6890
|
+
resizeObserver.observe(terminalRef.current);
|
|
6891
|
+
}
|
|
6892
|
+
return () => {
|
|
6893
|
+
resizeObserver.disconnect();
|
|
6894
|
+
};
|
|
6895
|
+
}, []);
|
|
6896
|
+
return jsxRuntime.jsx(TerminalWrapper, { ref: terminalRef });
|
|
6897
|
+
};
|
|
6898
|
+
|
|
6785
6899
|
const STATUS_TRANSLATION_KEYS = {
|
|
6786
|
-
[api.RemoteTaskStatus.Process]: "
|
|
6787
|
-
[api.RemoteTaskStatus.Completed]: "
|
|
6788
|
-
[api.RemoteTaskStatus.Error]: "
|
|
6789
|
-
[api.RemoteTaskStatus.Unknown]: "
|
|
6900
|
+
[api.RemoteTaskStatus.Process]: "taskProcess",
|
|
6901
|
+
[api.RemoteTaskStatus.Completed]: "taskCompleted",
|
|
6902
|
+
[api.RemoteTaskStatus.Error]: "taskError",
|
|
6903
|
+
[api.RemoteTaskStatus.Unknown]: "taskUnknown",
|
|
6790
6904
|
};
|
|
6791
6905
|
const STATUS_DEFAULT_VALUES = {
|
|
6792
|
-
[api.RemoteTaskStatus.Process]: "
|
|
6906
|
+
[api.RemoteTaskStatus.Process]: "Остановить",
|
|
6793
6907
|
[api.RemoteTaskStatus.Completed]: "Завершено",
|
|
6794
6908
|
[api.RemoteTaskStatus.Error]: "Ошибка",
|
|
6795
6909
|
[api.RemoteTaskStatus.Unknown]: "Неизвестно",
|
|
@@ -6803,12 +6917,6 @@ const STATUS_COLORS = {
|
|
|
6803
6917
|
|
|
6804
6918
|
const LogDialog = ({ isOpen, onClose, logs, status, statusColors }) => {
|
|
6805
6919
|
const { t } = useGlobalContext();
|
|
6806
|
-
const contentRef = React.useRef(null);
|
|
6807
|
-
React.useEffect(() => {
|
|
6808
|
-
if (contentRef.current) {
|
|
6809
|
-
contentRef.current.scrollTop = 0;
|
|
6810
|
-
}
|
|
6811
|
-
}, [logs]);
|
|
6812
6920
|
const getStatusText = React.useCallback((status) => {
|
|
6813
6921
|
const translationKey = STATUS_TRANSLATION_KEYS[status] || STATUS_TRANSLATION_KEYS[api.RemoteTaskStatus.Unknown];
|
|
6814
6922
|
const defaultValue = STATUS_DEFAULT_VALUES[status] || STATUS_DEFAULT_VALUES[api.RemoteTaskStatus.Unknown];
|
|
@@ -6817,7 +6925,7 @@ const LogDialog = ({ isOpen, onClose, logs, status, statusColors }) => {
|
|
|
6817
6925
|
const getStatusColor = React.useCallback((status) => {
|
|
6818
6926
|
return statusColors?.[status] || STATUS_COLORS[status] || STATUS_COLORS[api.RemoteTaskStatus.Unknown];
|
|
6819
6927
|
}, [statusColors]);
|
|
6820
|
-
return (jsxRuntime.jsxs(uilibGl.Dialog, { isOpen: isOpen, onCloseRequest: onClose, modal: true, maxWidth: "800px", minWidth: "600px", minHeight: "400px", children: [jsxRuntime.jsx(uilibGl.DialogTitle, { children: jsxRuntime.jsxs(uilibGl.Flex, { justifyContent: "space-between", alignItems: "center", children: [jsxRuntime.jsx("span", { children: t("
|
|
6928
|
+
return (jsxRuntime.jsxs(uilibGl.Dialog, { isOpen: isOpen, onCloseRequest: onClose, modal: true, maxWidth: "800px", minWidth: "600px", minHeight: "400px", children: [jsxRuntime.jsx(uilibGl.DialogTitle, { children: jsxRuntime.jsxs(uilibGl.Flex, { justifyContent: "space-between", alignItems: "center", children: [jsxRuntime.jsx("span", { children: t("taskLogs", { ns: "dashboard", defaultValue: "Логи выполнения задачи" }) }), jsxRuntime.jsx(uilibGl.IconButton, { kind: "close", onClick: onClose })] }) }), jsxRuntime.jsx(uilibGl.DialogContent, { children: jsxRuntime.jsxs(uilibGl.Flex, { flexDirection: "column", height: "100%", marginBottom: "2rem", children: [jsxRuntime.jsxs(StatusBadge, { "$statusColor": getStatusColor(status), children: [t("taskStatus", { ns: "dashboard", defaultValue: "Статус" }), ": ", getStatusText(status)] }), jsxRuntime.jsx(LogTerminal, { log: logs || t("taskLogsEmpty", { ns: "dashboard", defaultValue: "Логи отсутствуют" }) })] }) })] }));
|
|
6821
6929
|
};
|
|
6822
6930
|
|
|
6823
6931
|
const StatusWaitingButton = styled(uilibGl.WaitingButton) `
|
|
@@ -6831,14 +6939,18 @@ const StatusWaitingButton = styled(uilibGl.WaitingButton) `
|
|
|
6831
6939
|
`}
|
|
6832
6940
|
`;
|
|
6833
6941
|
|
|
6834
|
-
const TaskContainer = React.memo(({ type, elementConfig }) => {
|
|
6942
|
+
const TaskContainer = React.memo(({ type, elementConfig, renderElement }) => {
|
|
6835
6943
|
const { t, ewktGeometry } = useGlobalContext();
|
|
6836
6944
|
const { dataSources, filters: selectedFilters } = useWidgetContext(type);
|
|
6837
6945
|
const { currentPage } = useWidgetPage(type);
|
|
6838
|
-
const { taskId, runTask, stopTask, status, openLog, loading, isLogDialogOpen, closeLog,
|
|
6946
|
+
const { taskId, runTask, stopTask, status, openLog, loading, isLogDialogOpen, closeLog, log } = usePythonTask();
|
|
6839
6947
|
const { options } = elementConfig || {};
|
|
6840
6948
|
const { title, relatedResources, center, icon, statusColors } = options || {};
|
|
6841
6949
|
const onClick = React.useCallback(async () => {
|
|
6950
|
+
if (taskId) {
|
|
6951
|
+
await stopTask();
|
|
6952
|
+
return;
|
|
6953
|
+
}
|
|
6842
6954
|
await Promise.all(relatedResources.map(({ resourceId, parameters, script, fileName, methodName }) => {
|
|
6843
6955
|
const newParams = applyQueryFilters({
|
|
6844
6956
|
parameters,
|
|
@@ -6849,8 +6961,8 @@ const TaskContainer = React.memo(({ type, elementConfig }) => {
|
|
|
6849
6961
|
});
|
|
6850
6962
|
return runTask({ resourceId, parameters: newParams, script, fileName, methodName });
|
|
6851
6963
|
}));
|
|
6852
|
-
}, [currentPage.filters, dataSources, ewktGeometry, relatedResources, runTask, selectedFilters]);
|
|
6853
|
-
return (jsxRuntime.
|
|
6964
|
+
}, [currentPage.filters, dataSources, ewktGeometry, relatedResources, runTask, selectedFilters, stopTask, taskId]);
|
|
6965
|
+
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(ExpandableTitle, { elementConfig: elementConfig, type: type, renderElement: renderElement }), jsxRuntime.jsxs(uilibGl.Flex, { justifyContent: center ? "center" : "flex-start", children: [jsxRuntime.jsxs(StatusWaitingButton, { primary: true, status: status, statusColors: statusColors, isWaiting: loading || !!taskId, disabled: !relatedResources?.length, onClick: onClick, children: [icon && jsxRuntime.jsx(uilibGl.FlexSpan, { marginRight: "0.5rem", children: jsxRuntime.jsx(uilibGl.Icon, { kind: icon }) }), title || t("run", { ns: "dashboard", defaultValue: "Запуск" })] }), !!taskId && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(uilibGl.IconButton, { kind: "info", onClick: openLog }), jsxRuntime.jsx(LogDialog, { logs: log, status: status, statusColors: statusColors, isOpen: isLogDialogOpen, onClose: closeLog })] }))] })] }));
|
|
6854
6966
|
});
|
|
6855
6967
|
|
|
6856
6968
|
const containerComponents = {
|
|
@@ -7680,27 +7792,30 @@ const StyledIcon = styled(uilibGl.Icon) `
|
|
|
7680
7792
|
|
|
7681
7793
|
const ElementIcon = React.memo(({ type, elementConfig }) => {
|
|
7682
7794
|
const { attributes } = useWidgetContext(type);
|
|
7683
|
-
const { value, attributeName, options } = elementConfig || {};
|
|
7795
|
+
const { value, attributeName, options, style } = elementConfig || {};
|
|
7684
7796
|
const { fontSize, fontColor } = options || {};
|
|
7685
7797
|
const iconValue = React.useMemo(() => (attributeName ? attributes?.find(item => item.name === attributeName)?.value : value), [attributeName, attributes, value]);
|
|
7686
|
-
return jsxRuntime.jsx(StyledIcon, { kind: iconValue, fontSize: fontSize, fontColor: fontColor });
|
|
7798
|
+
return jsxRuntime.jsx(StyledIcon, { kind: iconValue, fontSize: fontSize, fontColor: fontColor, style: style });
|
|
7687
7799
|
});
|
|
7688
7800
|
|
|
7689
7801
|
const ElementImage = React.memo(({ type, elementConfig }) => {
|
|
7690
7802
|
const { attributes } = useWidgetContext(type);
|
|
7691
|
-
const { value, attributeName, options } = elementConfig || {};
|
|
7803
|
+
const { value, attributeName, options, style } = elementConfig || {};
|
|
7692
7804
|
const { width } = options || {};
|
|
7693
7805
|
const firstImage = React.useMemo(() => {
|
|
7694
7806
|
if (value) {
|
|
7695
|
-
return value.toString();
|
|
7807
|
+
return getResourceUrl(value.toString());
|
|
7696
7808
|
}
|
|
7697
7809
|
if (!attributeName || Array.isArray(attributeName)) {
|
|
7698
7810
|
return null;
|
|
7699
7811
|
}
|
|
7700
7812
|
const attribute = attributes?.find(item => item.name === attributeName);
|
|
7701
|
-
|
|
7813
|
+
const imageUrl = attribute?.value?.split(";")?.[0];
|
|
7814
|
+
if (!imageUrl)
|
|
7815
|
+
return null;
|
|
7816
|
+
return getResourceUrl(imageUrl);
|
|
7702
7817
|
}, [attributeName, attributes, value]);
|
|
7703
|
-
return firstImage ? jsxRuntime.jsx("img", { src: firstImage, alt: firstImage, width: width }) : null;
|
|
7818
|
+
return firstImage ? jsxRuntime.jsx("img", { src: firstImage, alt: firstImage, width: width, style: style }) : null;
|
|
7704
7819
|
});
|
|
7705
7820
|
|
|
7706
7821
|
const ElementLegend = React.memo(({ type, element, elementConfig }) => {
|