@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/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 [result, setResult] = useState(null);
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 resultRef = useRef(null);
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
- setResult(null);
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
- setResult(t("taskRunFail", { ns: "devMode", defaultValue: "Не удалось запустить задачу" }));
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
- setResult([data.log, resultRef.current].filter(Boolean).join("\n\n"));
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
- resultRef.current = result;
4917
- }, [result]);
4917
+ logRef.current = log;
4918
+ }, [log]);
4918
4919
  return {
4919
4920
  taskId,
4920
4921
  runTask,
4921
4922
  stopTask,
4922
4923
  openLog,
4923
- result,
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
- const LogContainer = styled.div `
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]: "task.status.process",
6785
- [RemoteTaskStatus.Completed]: "task.status.completed",
6786
- [RemoteTaskStatus.Error]: "task.status.error",
6787
- [RemoteTaskStatus.Unknown]: "task.status.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("task.logs.title", { 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("task.status.label", { ns: "dashboard", defaultValue: "Статус" }), ": ", getStatusText(status)] }), jsx(LogContainer, { ref: contentRef, children: logs || t("task.logs.empty", { ns: "dashboard", defaultValue: "Логи отсутствуют" }) })] }) })] }));
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, result } = usePythonTask();
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 (jsx(Fragment$1, { children: jsxs(Flex, { justifyContent: center ? "center" : "flex-start", children: [jsxs(StatusWaitingButton, { primary: true, status: status, statusColors: statusColors, isWaiting: loading, disabled: !relatedResources?.length, onClick: onClick, children: [icon && jsx(FlexSpan, { marginRight: "0.5rem", children: jsx(Icon, { kind: icon }) }), title || t("run", { ns: "dashboard", defaultValue: "Запуск" })] }), !!taskId && (jsx(IconButton, { kind: "stop", onClick: stopTask })), !!result && (jsxs(Fragment$1, { children: [jsx(IconButton, { kind: "info", onClick: openLog }), jsx(LogDialog, { isOpen: isLogDialogOpen, onClose: closeLog, logs: result, status: status, statusColors: statusColors })] }))] }) }));
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
- return attribute?.value?.split(";")?.[0];
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 }) => {