@evergis/react 3.1.65 → 3.1.67

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.
@@ -0,0 +1,4 @@
1
+ import { RemoteTaskStatus } from '@evergis/api';
2
+ export declare const STATUS_TRANSLATION_KEYS: Partial<Record<RemoteTaskStatus, string>>;
3
+ export declare const STATUS_DEFAULT_VALUES: Partial<Record<RemoteTaskStatus, string>>;
4
+ export declare const STATUS_COLORS: Partial<Record<RemoteTaskStatus, string>>;
@@ -0,0 +1,3 @@
1
+ import { FC } from 'react';
2
+ import { LogDialogProps } from './types';
3
+ export declare const LogDialog: FC<LogDialogProps>;
@@ -0,0 +1,4 @@
1
+ export declare const StatusBadge: import('styled-components').StyledComponent<"div", any, import('@evergis/uilib-gl').FlexProps & {
2
+ $statusColor: string;
3
+ }, never>;
4
+ export declare const LogContainer: import('styled-components').StyledComponent<"div", any, {}, never>;
@@ -0,0 +1,8 @@
1
+ import { RemoteTaskStatus } from '@evergis/api';
2
+ export interface LogDialogProps {
3
+ isOpen: boolean;
4
+ onClose: () => void;
5
+ logs: string | null;
6
+ status: RemoteTaskStatus;
7
+ statusColors?: Record<RemoteTaskStatus, string>;
8
+ }
@@ -0,0 +1,5 @@
1
+ import { RemoteTaskStatus } from '@evergis/api';
2
+ export declare const StatusWaitingButton: import('styled-components').StyledComponent<import('react').FC<import('@evergis/uilib-gl').IWaitingButton>, any, {
3
+ status?: RemoteTaskStatus;
4
+ statusColors?: Record<RemoteTaskStatus, string>;
5
+ }, never>;
@@ -1,11 +1,11 @@
1
1
  import { CSSProperties, FC, ReactNode } from 'react';
2
2
  import { Projection } from 'mapbox-gl';
3
- import { AttributeDefinitionDc, AttributeFormatDefinitionDc, AttributeType, EqlRequestDc, FeatureDc, GeometryType, PagedFeaturesListDc, PositionDc, StringSubType } from '@evergis/api';
3
+ import { i18n } from 'i18next';
4
+ import { AttributeDefinitionDc, AttributeFormatDefinitionDc, AttributeType, EqlRequestDc, FeatureDc, GeometryType, PagedFeaturesListDc, PositionDc, RemoteTaskStatus, StringSubType } from '@evergis/api';
4
5
  import { PieChartData, BarChartMarker } from '@evergis/charts';
5
6
  import { IOption } from '@evergis/uilib-gl';
6
7
  import { FeatureAttributeValue, EditGeometryType, LayerInfo, ThemeName } from '../../types';
7
8
  import { InnerContainerProps } from './containers/DataSourceInnerContainer/types';
8
- import { i18n } from 'i18next';
9
9
  export interface DashboardsProps {
10
10
  zIndex?: CSSProperties["zIndex"];
11
11
  }
@@ -46,6 +46,7 @@ export interface ConfigOptions {
46
46
  relatedDataSources?: ConfigRelatedDataSource[];
47
47
  relatedAttributes?: ConfigRelatedAttribute[];
48
48
  relatedResources?: ConfigRelatedResource[];
49
+ statusColors?: Record<RemoteTaskStatus, string>;
49
50
  chartType?: "bar" | "line" | "pie" | "stack";
50
51
  layerNames?: string[];
51
52
  layerName?: string;
@@ -1,4 +1,6 @@
1
+ import { RemoteTaskStatus } from '@evergis/api';
1
2
  export declare const usePythonTask: () => {
3
+ taskId: string;
2
4
  runTask: ({ resourceId, parameters, script, fileName, methodName, }: {
3
5
  resourceId: string;
4
6
  parameters: Record<string, unknown>;
@@ -7,8 +9,12 @@ export declare const usePythonTask: () => {
7
9
  methodName?: string;
8
10
  }) => Promise<void>;
9
11
  stopTask: () => Promise<void>;
12
+ openLog: () => void;
10
13
  result: string;
11
14
  error: Error;
15
+ status: RemoteTaskStatus;
12
16
  loading: boolean;
13
17
  executionTime: number;
18
+ isLogDialogOpen: boolean;
19
+ closeLog: () => void;
14
20
  };
package/dist/index.js CHANGED
@@ -4827,22 +4827,28 @@ const SERVER_NOTIFICATION_EVENT = {
4827
4827
  const usePythonTask = () => {
4828
4828
  const { api: api$1, t } = useGlobalContext();
4829
4829
  const { addSubscription, connection, unsubscribeById } = useServerNotificationsContext();
4830
+ const [status, setStatus] = React.useState(api.RemoteTaskStatus.Unknown);
4830
4831
  const [result, setResult] = React.useState(null);
4831
4832
  const [error, setError] = React.useState(null);
4832
4833
  const [loading, setLoading] = React.useState(false);
4833
4834
  const [executionTime, setExecutionTime] = React.useState(null);
4834
4835
  const [taskId, setTaskId] = React.useState(null);
4835
4836
  const [subscriptionId, setSubscriptionId] = React.useState(null);
4837
+ const [isLogDialogOpen, setIsLogDialogOpen] = React.useState(false);
4838
+ const resultRef = React.useRef(null);
4836
4839
  const reset = React.useCallback(() => {
4837
- setResult(null);
4840
+ setStatus(api.RemoteTaskStatus.Unknown);
4838
4841
  setError(null);
4839
- setLoading(true);
4842
+ setLoading(false);
4840
4843
  setExecutionTime(null);
4841
4844
  setTaskId(null);
4842
4845
  setSubscriptionId(null);
4843
4846
  }, []);
4844
4847
  const runTask = React.useCallback(async ({ resourceId, parameters, script, fileName, methodName, }) => {
4845
4848
  reset();
4849
+ setStatus(api.RemoteTaskStatus.Process);
4850
+ setResult(null);
4851
+ setLoading(true);
4846
4852
  const start = Date.now();
4847
4853
  let prototypeId = await api$1.remoteTaskManager.createTaskPrototype({
4848
4854
  enabled: true,
@@ -4865,6 +4871,7 @@ const usePythonTask = () => {
4865
4871
  prototypeId = prototypeId.replace(/["']+/g, "");
4866
4872
  const { id: newTaskId, success } = await api$1.remoteTaskManager.startTask1(prototypeId);
4867
4873
  if (!success) {
4874
+ setStatus(api.RemoteTaskStatus.Error);
4868
4875
  setResult(t("taskRunFail", { ns: "devMode", defaultValue: "Не удалось запустить задачу" }));
4869
4876
  setExecutionTime(Date.now() - start);
4870
4877
  setLoading(false);
@@ -4877,7 +4884,8 @@ const usePythonTask = () => {
4877
4884
  setSubscriptionId(newSubscriptionId);
4878
4885
  const onNotification = ({ data }) => {
4879
4886
  if (data?.taskId === newTaskId) {
4880
- setResult(`${data ? `${data}\n` : ""}${data.log || ""}`);
4887
+ setStatus(data.status);
4888
+ setResult([data.log, resultRef.current].filter(Boolean).join("\n\n"));
4881
4889
  setError(null);
4882
4890
  setExecutionTime(Date.now() - start);
4883
4891
  setLoading(false);
@@ -4900,7 +4908,28 @@ const usePythonTask = () => {
4900
4908
  reset();
4901
4909
  unsubscribeById(subscriptionId);
4902
4910
  }, [api$1, reset, unsubscribeById, taskId, subscriptionId]);
4903
- return { runTask, stopTask, result, error, loading, executionTime };
4911
+ const openLog = React.useCallback(() => {
4912
+ setIsLogDialogOpen(true);
4913
+ }, []);
4914
+ const closeLog = React.useCallback(() => {
4915
+ setIsLogDialogOpen(false);
4916
+ }, []);
4917
+ React.useEffect(() => {
4918
+ resultRef.current = result;
4919
+ }, [result]);
4920
+ return {
4921
+ taskId,
4922
+ runTask,
4923
+ stopTask,
4924
+ openLog,
4925
+ result,
4926
+ error,
4927
+ status,
4928
+ loading,
4929
+ executionTime,
4930
+ isLogDialogOpen,
4931
+ closeLog,
4932
+ };
4904
4933
  };
4905
4934
 
4906
4935
  const useAppHeight = () => {
@@ -6732,13 +6761,83 @@ const UploadContainer = React.memo(({ type, elementConfig, renderElement }) => {
6732
6761
  return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(ExpandableTitle, { elementConfig: elementConfig, type: type, renderElement: renderElement }), isVisible && renderElement({ id: "uploader", wrap: false })] }));
6733
6762
  });
6734
6763
 
6764
+ const StatusBadge = styled(uilibGl.Flex) `
6765
+ margin-bottom: 1rem;
6766
+ padding: 0.5rem 1rem;
6767
+ background-color: ${({ $statusColor }) => $statusColor};
6768
+ color: ${({ theme }) => theme.palette.iconContrast};
6769
+ border-radius: 0.25rem;
6770
+ `;
6771
+ const LogContainer = styled.div `
6772
+ flex: 1;
6773
+ padding: 1rem;
6774
+ background-color: ${({ theme }) => theme.palette.element};
6775
+ color: ${({ theme }) => theme.palette.textPrimary};
6776
+ font-family: monospace;
6777
+ font-size: 0.875rem;
6778
+ overflow: auto;
6779
+ white-space: pre-wrap;
6780
+ word-break: break-word;
6781
+ border-radius: 0.25rem;
6782
+ max-height: 31.25rem;
6783
+ `;
6784
+
6785
+ const STATUS_TRANSLATION_KEYS = {
6786
+ [api.RemoteTaskStatus.Process]: "task.status.process",
6787
+ [api.RemoteTaskStatus.Completed]: "task.status.completed",
6788
+ [api.RemoteTaskStatus.Error]: "task.status.error",
6789
+ [api.RemoteTaskStatus.Unknown]: "task.status.unknown",
6790
+ };
6791
+ const STATUS_DEFAULT_VALUES = {
6792
+ [api.RemoteTaskStatus.Process]: "Выполняется...",
6793
+ [api.RemoteTaskStatus.Completed]: "Завершено",
6794
+ [api.RemoteTaskStatus.Error]: "Ошибка",
6795
+ [api.RemoteTaskStatus.Unknown]: "Неизвестно",
6796
+ };
6797
+ const STATUS_COLORS = {
6798
+ [api.RemoteTaskStatus.Process]: "#2196f3",
6799
+ [api.RemoteTaskStatus.Completed]: "#4caf50",
6800
+ [api.RemoteTaskStatus.Error]: "#f44336",
6801
+ [api.RemoteTaskStatus.Unknown]: "#757575",
6802
+ };
6803
+
6804
+ const LogDialog = ({ isOpen, onClose, logs, status, statusColors }) => {
6805
+ 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
+ const getStatusText = React.useCallback((status) => {
6813
+ const translationKey = STATUS_TRANSLATION_KEYS[status] || STATUS_TRANSLATION_KEYS[api.RemoteTaskStatus.Unknown];
6814
+ const defaultValue = STATUS_DEFAULT_VALUES[status] || STATUS_DEFAULT_VALUES[api.RemoteTaskStatus.Unknown];
6815
+ return t(translationKey, { ns: "dashboard", defaultValue });
6816
+ }, [t]);
6817
+ const getStatusColor = React.useCallback((status) => {
6818
+ return statusColors?.[status] || STATUS_COLORS[status] || STATUS_COLORS[api.RemoteTaskStatus.Unknown];
6819
+ }, [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("task.logs.title", { 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("task.status.label", { ns: "dashboard", defaultValue: "Статус" }), ": ", getStatusText(status)] }), jsxRuntime.jsx(LogContainer, { ref: contentRef, children: logs || t("task.logs.empty", { ns: "dashboard", defaultValue: "Логи отсутствуют" }) })] }) })] }));
6821
+ };
6822
+
6823
+ const StatusWaitingButton = styled(uilibGl.WaitingButton) `
6824
+ ${({ status = api.RemoteTaskStatus.Unknown, statusColors }) => !!statusColors?.[status] && styled.css `
6825
+ transition: background-color ${uilibGl.transition.toggle};
6826
+ background-color: ${statusColors[status]};
6827
+
6828
+ :hover {
6829
+ background-color: ${statusColors[status]};
6830
+ }
6831
+ `}
6832
+ `;
6833
+
6735
6834
  const TaskContainer = React.memo(({ type, elementConfig }) => {
6736
6835
  const { t, ewktGeometry } = useGlobalContext();
6737
6836
  const { dataSources, filters: selectedFilters } = useWidgetContext(type);
6738
6837
  const { currentPage } = useWidgetPage(type);
6739
- const { runTask, loading } = usePythonTask();
6838
+ const { taskId, runTask, stopTask, status, openLog, loading, isLogDialogOpen, closeLog, result } = usePythonTask();
6740
6839
  const { options } = elementConfig || {};
6741
- const { title, relatedResources, center, icon } = options || {};
6840
+ const { title, relatedResources, center, icon, statusColors } = options || {};
6742
6841
  const onClick = React.useCallback(async () => {
6743
6842
  await Promise.all(relatedResources.map(({ resourceId, parameters, script, fileName, methodName }) => {
6744
6843
  const newParams = applyQueryFilters({
@@ -6751,7 +6850,7 @@ const TaskContainer = React.memo(({ type, elementConfig }) => {
6751
6850
  return runTask({ resourceId, parameters: newParams, script, fileName, methodName });
6752
6851
  }));
6753
6852
  }, [currentPage.filters, dataSources, ewktGeometry, relatedResources, runTask, selectedFilters]);
6754
- return (jsxRuntime.jsx(uilibGl.Flex, { justifyContent: center ? "center" : "flex-start", children: jsxRuntime.jsxs(uilibGl.WaitingButton, { primary: true, isWaiting: loading, 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: "Запуск" })] }) }));
6853
+ return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: jsxRuntime.jsxs(uilibGl.Flex, { justifyContent: center ? "center" : "flex-start", children: [jsxRuntime.jsxs(StatusWaitingButton, { primary: true, status: status, statusColors: statusColors, isWaiting: loading, 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.jsx(uilibGl.IconButton, { kind: "stop", onClick: stopTask })), !!result && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(uilibGl.IconButton, { kind: "info", onClick: openLog }), jsxRuntime.jsx(LogDialog, { isOpen: isLogDialogOpen, onClose: closeLog, logs: result, status: status, statusColors: statusColors })] }))] }) }));
6755
6854
  });
6756
6855
 
6757
6856
  const containerComponents = {