@evergis/react 3.1.49 → 3.1.51

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,3 @@
1
+ import { FC } from 'react';
2
+ import { ContainerProps } from '../../types';
3
+ export declare const TaskContainer: FC<ContainerProps>;
@@ -0,0 +1,2 @@
1
+ export declare const UploaderContainer: import('styled-components').StyledComponent<any, any, object, string | number | symbol>;
2
+ export declare const UploaderTitle: import('styled-components').StyledComponent<"div", any, import('@evergis/uilib-gl').FlexProps, never>;
@@ -0,0 +1,3 @@
1
+ import { FC } from 'react';
2
+ import { ContainerProps } from '../../types';
3
+ export declare const UploadContainer: FC<ContainerProps>;
@@ -0,0 +1,2 @@
1
+ export declare const UploaderContainer: import('styled-components').StyledComponent<any, any, object, string | number | symbol>;
2
+ export declare const UploaderTitle: import('styled-components').StyledComponent<"div", any, import('@evergis/uilib-gl').FlexProps, never>;
@@ -19,3 +19,4 @@ export * from './SlideshowContainer';
19
19
  export * from './TabsContainer';
20
20
  export * from './TitleContainer';
21
21
  export * from './TwoColumnContainer';
22
+ export * from './UploadContainer';
@@ -27,5 +27,7 @@ export declare const containerComponents: {
27
27
  readonly AddFeature: import('react').FC<import('../types').ContainerProps>;
28
28
  readonly Divider: import('react').FC<import('../types').ContainerProps>;
29
29
  readonly ExportPdf: import('react').FC<import('../types').ContainerProps>;
30
+ readonly Upload: import('react').FC<import('../types').ContainerProps>;
31
+ readonly Task: import('react').FC<import('../types').ContainerProps>;
30
32
  readonly default: import('react').FC<import('../types').ContainerProps>;
31
33
  };
@@ -27,6 +27,11 @@ export interface ConfigRelatedDataSource {
27
27
  hideAxis?: boolean;
28
28
  filterName?: string;
29
29
  }
30
+ export interface ConfigRelatedResource {
31
+ resourceId: string;
32
+ parameters: Record<string, unknown>;
33
+ script?: string;
34
+ }
30
35
  export interface BaseMapSettings {
31
36
  opacity?: number;
32
37
  showBuildings?: boolean;
@@ -38,10 +43,13 @@ export interface ConfigOptions {
38
43
  themeName?: ThemeName;
39
44
  relatedDataSources?: ConfigRelatedDataSource[];
40
45
  relatedAttributes?: ConfigRelatedAttribute[];
46
+ relatedResources?: ConfigRelatedResource[];
41
47
  chartType?: "bar" | "line" | "pie" | "stack";
42
48
  layerNames?: string[];
43
49
  layerName?: string;
44
50
  geometryType?: GeometryType | EditGeometryType;
51
+ fileExtensions?: string;
52
+ parentResourceId?: string;
45
53
  srid?: string;
46
54
  title?: string;
47
55
  label?: string;
@@ -253,6 +261,8 @@ export declare enum ContainerTemplate {
253
261
  AddFeature = "AddFeature",
254
262
  Slideshow = "Slideshow",
255
263
  ExportPdf = "ExportPdf",
264
+ Upload = "Upload",
265
+ Task = "Task",
256
266
  Divider = "Divider"
257
267
  }
258
268
  export declare enum HeaderTemplate {
@@ -1,5 +1,6 @@
1
1
  export * from './map';
2
2
  export * from './serverNotifications';
3
+ export * from './task';
3
4
  export * from './useAppHeight';
4
5
  export * from './useDebouncedCallback';
5
6
  export * from './useToggle';
@@ -0,0 +1,4 @@
1
+ export declare const SERVER_NOTIFICATION_EVENT: {
2
+ ReceiveFeaturesUpdate: string;
3
+ PythonProgressNotification: string;
4
+ };
@@ -0,0 +1,3 @@
1
+ export * from './constants';
2
+ export * from './usePythonTask';
3
+ export * from './types';
@@ -0,0 +1,16 @@
1
+ import { RemoteTaskStatus } from '@evergis/api';
2
+ export interface PythonTaskProgressSubscription {
3
+ tag: "python_task_progress_event";
4
+ taskId: string;
5
+ }
6
+ export interface PythonTaskProgressNotification {
7
+ data: {
8
+ log: string;
9
+ progress: number;
10
+ status: RemoteTaskStatus;
11
+ subTaskId?: string;
12
+ taskId: string;
13
+ };
14
+ tag: string;
15
+ createdAt: string;
16
+ }
@@ -0,0 +1,12 @@
1
+ export declare const usePythonTask: () => {
2
+ runTask: ({ resourceId, parameters, script, }: {
3
+ resourceId: string;
4
+ parameters: Record<string, unknown>;
5
+ script: string;
6
+ }) => Promise<void>;
7
+ stopTask: () => Promise<void>;
8
+ result: string;
9
+ error: Error;
10
+ loading: boolean;
11
+ executionTime: number;
12
+ };
package/dist/index.js CHANGED
@@ -3382,6 +3382,8 @@ exports.ContainerTemplate = void 0;
3382
3382
  ContainerTemplate["AddFeature"] = "AddFeature";
3383
3383
  ContainerTemplate["Slideshow"] = "Slideshow";
3384
3384
  ContainerTemplate["ExportPdf"] = "ExportPdf";
3385
+ ContainerTemplate["Upload"] = "Upload";
3386
+ ContainerTemplate["Task"] = "Task";
3385
3387
  ContainerTemplate["Divider"] = "Divider";
3386
3388
  })(exports.ContainerTemplate || (exports.ContainerTemplate = {}));
3387
3389
  exports.HeaderTemplate = void 0;
@@ -4994,6 +4996,88 @@ const useServerNotificationsContext = () => {
4994
4996
  return React.useContext(ServerNotificationsContext);
4995
4997
  };
4996
4998
 
4999
+ const SERVER_NOTIFICATION_EVENT = {
5000
+ ReceiveFeaturesUpdate: "ReceiveFeaturesUpdateNotification",
5001
+ PythonProgressNotification: "ReceivePythonProgressNotification",
5002
+ };
5003
+
5004
+ const usePythonTask = () => {
5005
+ const { api: api$1, t } = useGlobalContext();
5006
+ const { addSubscription, connection, unsubscribeById } = useServerNotificationsContext();
5007
+ const [result, setResult] = React.useState(null);
5008
+ const [error, setError] = React.useState(null);
5009
+ const [loading, setLoading] = React.useState(false);
5010
+ const [executionTime, setExecutionTime] = React.useState(null);
5011
+ const [taskId, setTaskId] = React.useState(null);
5012
+ const [subscriptionId, setSubscriptionId] = React.useState(null);
5013
+ const reset = React.useCallback(() => {
5014
+ setResult(null);
5015
+ setError(null);
5016
+ setLoading(true);
5017
+ setExecutionTime(null);
5018
+ setTaskId(null);
5019
+ setSubscriptionId(null);
5020
+ }, []);
5021
+ const runTask = React.useCallback(async ({ resourceId, parameters, script, }) => {
5022
+ reset();
5023
+ const start = Date.now();
5024
+ let prototypeId = await api$1.remoteTaskManager.createTaskPrototype({
5025
+ enabled: true,
5026
+ startIfPreviousError: true,
5027
+ startIfPreviousNotFinished: true,
5028
+ subTaskSettings: [
5029
+ {
5030
+ type: "pythonService",
5031
+ startParameters: {
5032
+ resourceId,
5033
+ parameters,
5034
+ script,
5035
+ method: "pythonrunner/run",
5036
+ },
5037
+ },
5038
+ ],
5039
+ });
5040
+ prototypeId = prototypeId.replace(/["']+/g, "");
5041
+ const { id: newTaskId, success } = await api$1.remoteTaskManager.startTask1(prototypeId);
5042
+ if (!success) {
5043
+ setResult(t("taskRunFail", { ns: "devMode", defaultValue: "Не удалось запустить задачу" }));
5044
+ setExecutionTime(Date.now() - start);
5045
+ setLoading(false);
5046
+ }
5047
+ const newSubscriptionId = await addSubscription({
5048
+ tag: "python_task_progress_event",
5049
+ taskId: newTaskId,
5050
+ });
5051
+ setTaskId(newTaskId);
5052
+ setSubscriptionId(newSubscriptionId);
5053
+ const onNotification = ({ data }) => {
5054
+ if (data?.taskId === newTaskId) {
5055
+ setResult(`${data ? `${data}\n` : ""}${data.log || ""}`);
5056
+ setError(null);
5057
+ setExecutionTime(Date.now() - start);
5058
+ setLoading(false);
5059
+ setTaskId([api.RemoteTaskStatus.Completed, api.RemoteTaskStatus.Error].includes(data.status)
5060
+ ? undefined
5061
+ : newTaskId);
5062
+ setSubscriptionId([api.RemoteTaskStatus.Completed, api.RemoteTaskStatus.Error].includes(data.status)
5063
+ ? undefined
5064
+ : newSubscriptionId);
5065
+ if ([api.RemoteTaskStatus.Completed, api.RemoteTaskStatus.Error].includes(data.status)) {
5066
+ unsubscribeById(newSubscriptionId);
5067
+ connection.off(SERVER_NOTIFICATION_EVENT.PythonProgressNotification, onNotification);
5068
+ }
5069
+ }
5070
+ };
5071
+ connection.on(SERVER_NOTIFICATION_EVENT.PythonProgressNotification, onNotification);
5072
+ }, [api$1, connection, addSubscription, unsubscribeById, reset]);
5073
+ const stopTask = React.useCallback(async () => {
5074
+ await api$1.remoteTaskManager.stop(taskId);
5075
+ reset();
5076
+ unsubscribeById(subscriptionId);
5077
+ }, [api$1, reset, unsubscribeById, taskId, subscriptionId]);
5078
+ return { runTask, stopTask, result, error, loading, executionTime };
5079
+ };
5080
+
4997
5081
  const useAppHeight = () => {
4998
5082
  React.useEffect(() => {
4999
5083
  const setAppHeight = () => {
@@ -5423,7 +5507,7 @@ const formatElementValue = ({ t, value, elementConfig, attributes, wrap, }) => {
5423
5507
  : valueOrDefault;
5424
5508
  if (!wrap)
5425
5509
  return resultValue;
5426
- return (jsxRuntime.jsxs(React.Fragment, { children: [tagView ? (jsxRuntime.jsx(DashboardChip$1, { text: resultValue, "$bgColor": bgColor, "$fontColor": fontColor, "$fontSize": fontSize, "$radius": radius, style: style })) : (jsxRuntime.jsx(ElementValueWrapper, { "data-id": id, "data-templatename": templateName, children: resultValue })), withDivider && jsxRuntime.jsx(uilibGl.Divider, {})] }, id));
5510
+ return (jsxRuntime.jsxs(React.Fragment, { children: [tagView ? (jsxRuntime.jsx(DashboardChip$1, { text: resultValue, "$bgColor": bgColor, "$fontColor": fontColor, "$fontSize": fontSize, "$radius": radius, style: style })) : (jsxRuntime.jsx(ElementValueWrapper, { "data-id": id, "data-templatename": templateName, style: style, children: resultValue })), withDivider && jsxRuntime.jsx(uilibGl.Divider, {})] }, id));
5427
5511
  };
5428
5512
 
5429
5513
  const getAttributeValue = (element, attributes) => {
@@ -5474,7 +5558,7 @@ const ContainersGroupContainer = React.memo(({ elementConfig, type, renderElemen
5474
5558
  const { column, expandable, expanded } = options || {};
5475
5559
  const isColumn = column === undefined || column;
5476
5560
  const isVisible = isVisibleContainer(id, expandable, expanded, expandedContainers);
5477
- return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(ExpandableTitle, { elementConfig: elementConfig, type: type, renderElement: renderElement }), isVisible && (jsxRuntime.jsx(Container, { id: id, style: style, isColumn: isColumn, children: jsxRuntime.jsx(ContainerChildren, { items: children, elementConfig: elementConfig, isColumn: isColumn, isMain: id?.startsWith(CONFIG_PAGE_ID), renderElement: renderElement }) }))] }));
5561
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(ExpandableTitle, { elementConfig: elementConfig, type: type, renderElement: renderElement }), isVisible && (jsxRuntime.jsx(Container, { id: id, isColumn: isColumn, children: jsxRuntime.jsx(ContainerChildren, { items: children, elementConfig: elementConfig, isColumn: isColumn, isMain: id?.startsWith(CONFIG_PAGE_ID), renderElement: renderElement }) }))] }));
5478
5562
  });
5479
5563
 
5480
5564
  const OneColumnContainer = React.memo(({ elementConfig, renderElement }) => {
@@ -6005,7 +6089,7 @@ const ChartContainer = React.memo(({ elementConfig, isVisible, type, renderEleme
6005
6089
  const hasItems = !!data[0]?.items?.length;
6006
6090
  if (!loading && !hasItems && hideEmpty)
6007
6091
  return null;
6008
- return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(ExpandableTitle, { elementConfig: elementConfig, type: type, renderElement: renderElement }), isVisible && (jsxRuntime.jsxs(Container, { id: id, style: style, isColumn: true, children: [aliasElement && jsxRuntime.jsx(ContainerAlias, { hasBottomMargin: true, children: renderElement({ id: "alias" }) }), jsxRuntime.jsx(ContainerValue, { column: !twoColumns, alignItems: "center", children: hasItems ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(ContainerChart, { children: renderElement({ id: "chart" }) }), jsxRuntime.jsx(ContainerLegend, { justifyContent: legendElement?.options?.center ? "center" : "flex-start", children: renderElement({ id: "legend" }) })] })) : (jsxRuntime.jsx(jsxRuntime.Fragment, { children: "\u2014" })) })] }))] }));
6092
+ return (jsxRuntime.jsxs(uilibGl.FlexSpan, { flexDirection: "column", children: [jsxRuntime.jsx(ExpandableTitle, { elementConfig: elementConfig, type: type, renderElement: renderElement }), isVisible && (jsxRuntime.jsxs(Container, { id: id, style: style, isColumn: true, children: [aliasElement && jsxRuntime.jsx(ContainerAlias, { hasBottomMargin: true, children: renderElement({ id: "alias" }) }), jsxRuntime.jsx(ContainerValue, { column: !twoColumns, alignItems: "center", children: hasItems ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(ContainerChart, { children: renderElement({ id: "chart" }) }), jsxRuntime.jsx(ContainerLegend, { justifyContent: legendElement?.options?.center ? "center" : "flex-start", children: renderElement({ id: "legend" }) })] })) : (jsxRuntime.jsx(jsxRuntime.Fragment, { children: "\u2014" })) })] }))] }));
6009
6093
  });
6010
6094
 
6011
6095
  const PagesContainer = React.memo(({ type = exports.WidgetType.Dashboard, noBorders }) => {
@@ -6637,6 +6721,98 @@ const ExportPdfContainer = React.memo(({ type, elementConfig }) => {
6637
6721
  return (jsxRuntime.jsx(Container, { id: id, style: style, children: jsxRuntime.jsx(uilibGl.IconButton, { kind: icon || "download", primary: true, disabled: loading, onClick: onExport, children: title ?? t("downloadPdf", { ns: "dashboard", defaultValue: "Скачать PDF" }) }) }));
6638
6722
  });
6639
6723
 
6724
+ const UploaderContainer = styled(Container) `
6725
+ ${uilibGl.UploaderItemArea} {
6726
+ overflow: visible;
6727
+ padding-top: 1rem;
6728
+ padding-bottom: 1rem;
6729
+ }
6730
+
6731
+ ${uilibGl.UploaderTitleWrapper} {
6732
+ top: 0;
6733
+ padding-top: 0;
6734
+ border: 0;
6735
+ }
6736
+ `;
6737
+ const UploaderTitle = styled(uilibGl.Flex) `
6738
+ flex-direction: column;
6739
+ align-items: center;
6740
+ width: 11rem;
6741
+ margin: 0 auto;
6742
+ text-align: center;
6743
+ font-size: 0.625rem;
6744
+ color: ${({ theme: { palette } }) => palette.textSecondary};
6745
+
6746
+ span[kind] {
6747
+ width: 1.5rem;
6748
+ height: 1.5rem;
6749
+ margin-bottom: 0.75rem;
6750
+
6751
+ :after {
6752
+ font-size: 1.5rem;
6753
+ color: ${({ theme: { palette } }) => palette.textSecondary};
6754
+ opacity: 0.12;
6755
+ }
6756
+ }
6757
+ `;
6758
+
6759
+ const DEFAULT_FILE_EXTENSIONS = ".txt,.csv,.py";
6760
+ const UploadContainer = React.memo(({ type, elementConfig, renderElement }) => {
6761
+ const { t, api } = useGlobalContext();
6762
+ const { changeFilters } = useWidgetContext(type);
6763
+ const [files, setFiles] = React.useState([]);
6764
+ const refInput = React.useRef();
6765
+ const { id, style, options } = elementConfig || {};
6766
+ const { icon, title, filterName, fileExtensions = DEFAULT_FILE_EXTENSIONS, parentResourceId, multiSelect } = options || {};
6767
+ const onUpload = React.useCallback(async (input) => {
6768
+ const files = Array.isArray(input) ? input : [input];
6769
+ const response = await Promise.all(files.map(file => {
6770
+ return api.file.upload(file, true, parentResourceId);
6771
+ }));
6772
+ const uploadedFiles = response.map(item => ({
6773
+ name: item.name,
6774
+ id: item.resourceId,
6775
+ done: true,
6776
+ }));
6777
+ setFiles(currentFiles => ([...uploadedFiles, ...currentFiles]));
6778
+ }, [parentResourceId]);
6779
+ const onDelete = React.useCallback(async (id) => {
6780
+ const index = files.findIndex(file => file.id === id);
6781
+ if (index === -1)
6782
+ return;
6783
+ const resourceId = files[index].id;
6784
+ await api.file.deleteResource(resourceId);
6785
+ setFiles(currentFiles => currentFiles.filter(({ id }) => id !== resourceId));
6786
+ }, [files]);
6787
+ const renderTitle = React.useMemo(() => {
6788
+ if (files.length)
6789
+ return null;
6790
+ return (jsxRuntime.jsxs(UploaderTitle, { children: [jsxRuntime.jsx(uilibGl.Icon, { kind: icon || "upload" }), jsxRuntime.jsx("div", { children: title ?? t("uploadTitle", {
6791
+ ns: "dashboard",
6792
+ defaultValue: "Перетащите файл сюда или нажмите, чтобы выбрать",
6793
+ }) })] }));
6794
+ }, [icon, t, title, files.length]);
6795
+ React.useEffect(() => {
6796
+ if (!filterName)
6797
+ return;
6798
+ changeFilters({ [filterName]: { value: files.map(({ id }) => id) } });
6799
+ }, [files]);
6800
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(ExpandableTitle, { elementConfig: elementConfig, type: type, renderElement: renderElement }), jsxRuntime.jsx(UploaderContainer, { id: id, style: style, children: jsxRuntime.jsx(uilibGl.Uploader, { title: renderTitle, accept: fileExtensions, fileItems: files, currentRef: refInput, isMultiple: multiSelect, onUpload: onUpload, onDelete: onDelete }) })] }));
6801
+ });
6802
+
6803
+ const TaskContainer = React.memo(({ elementConfig }) => {
6804
+ const { t } = useGlobalContext();
6805
+ const { runTask, error, result, executionTime, loading } = usePythonTask();
6806
+ const { options } = elementConfig || {};
6807
+ const { title, relatedResources } = options || {};
6808
+ const onClick = React.useCallback(async () => {
6809
+ await Promise.all(relatedResources.map(({ resourceId, parameters, script }) => {
6810
+ return runTask({ resourceId, parameters, script });
6811
+ }));
6812
+ }, [relatedResources, runTask]);
6813
+ return (jsxRuntime.jsx(uilibGl.WaitingButton, { primary: true, isWaiting: loading, disabled: !relatedResources?.length, onClick: onClick, children: title || t("run", { ns: "dashboard", defaultValue: "Запуск" }) }));
6814
+ });
6815
+
6640
6816
  const containerComponents = {
6641
6817
  [exports.ContainerTemplate.DefaultAttributes]: DefaultAttributesContainer,
6642
6818
  [exports.ContainerTemplate.Pages]: PagesContainer,
@@ -6658,6 +6834,8 @@ const containerComponents = {
6658
6834
  [exports.ContainerTemplate.AddFeature]: AddFeatureContainer,
6659
6835
  [exports.ContainerTemplate.Divider]: DividerContainer,
6660
6836
  [exports.ContainerTemplate.ExportPdf]: ExportPdfContainer,
6837
+ [exports.ContainerTemplate.Upload]: UploadContainer,
6838
+ [exports.ContainerTemplate.Task]: TaskContainer,
6661
6839
  default: ContainersGroupContainer,
6662
6840
  };
6663
6841
 
@@ -10699,6 +10877,7 @@ exports.PresentationPanelWrapper = PresentationPanelWrapper;
10699
10877
  exports.PresentationWrapper = PresentationWrapper;
10700
10878
  exports.ProgressContainer = ProgressContainer;
10701
10879
  exports.RoundedBackgroundContainer = RoundedBackgroundContainer;
10880
+ exports.SERVER_NOTIFICATION_EVENT = SERVER_NOTIFICATION_EVENT;
10702
10881
  exports.ServerNotificationsContext = ServerNotificationsContext;
10703
10882
  exports.ServerNotificationsProvider = ServerNotificationsProvider;
10704
10883
  exports.SlideshowContainer = SlideshowContainer;
@@ -10717,6 +10896,7 @@ exports.TitleContainer = TitleContainer;
10717
10896
  exports.TopContainer = TopContainer;
10718
10897
  exports.TopContainerButtons = TopContainerButtons;
10719
10898
  exports.TwoColumnContainer = TwoColumnContainer;
10899
+ exports.UploadContainer = UploadContainer;
10720
10900
  exports.addDataSource = addDataSource;
10721
10901
  exports.addDataSources = addDataSources;
10722
10902
  exports.applyFiltersToCondition = applyFiltersToCondition;
@@ -10825,6 +11005,7 @@ exports.useLayerParams = useLayerParams;
10825
11005
  exports.useMapContext = useMapContext;
10826
11006
  exports.useMapDraw = useMapDraw;
10827
11007
  exports.useProjectDashboardInit = useProjectDashboardInit;
11008
+ exports.usePythonTask = usePythonTask;
10828
11009
  exports.useRedrawLayer = useRedrawLayer;
10829
11010
  exports.useRelatedDataSourceAttributes = useRelatedDataSourceAttributes;
10830
11011
  exports.useRenderElement = useRenderElement;