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