@pushwoosh/smart-components-kit 1.0.24 → 1.0.26

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,18 @@
1
+ import React from 'react';
2
+ import { type TaskInfo_args, type GetLastTasksRequest, type Status } from '@pushwoosh/rpc-v2-http-api-data/pushwoosh_export_messages_v2';
3
+ export type ExportTask = {
4
+ id: number;
5
+ status: Status;
6
+ filename?: string;
7
+ args?: TaskInfo_args;
8
+ progressTitle: string;
9
+ resultSubTitle: string;
10
+ };
11
+ type ExportCsvBannerProps = {
12
+ exportTask: ExportTask | undefined;
13
+ updateExportTask: (exportTask: Partial<ExportTask>) => void;
14
+ showLastExportLocalStorageKey: string;
15
+ lastTasksParams?: GetLastTasksRequest;
16
+ };
17
+ export declare const ExportCsvBanner: ({ exportTask, updateExportTask, showLastExportLocalStorageKey, lastTasksParams, }: ExportCsvBannerProps) => React.JSX.Element | null;
18
+ export {};
@@ -0,0 +1,121 @@
1
+ import React, { useEffect } from 'react';
2
+ import { ExportResult, ProgressBar, Button } from '@pushwoosh/dumb-components';
3
+ import { Color, ShapeRadius } from '@pushwoosh/kit-constants';
4
+ import { Horizontal } from '@pushwoosh/kit-helpers';
5
+ import { DangerTriangleIcon, DownloadIcon } from '@pushwoosh/kit-icons';
6
+ import { useExportService } from '../useExportService';
7
+ import { useExportVisibility } from './hooks/useExportVisibility';
8
+ export const ExportCsvBanner = ({
9
+ exportTask,
10
+ updateExportTask,
11
+ showLastExportLocalStorageKey,
12
+ lastTasksParams
13
+ }) => {
14
+ const {
15
+ getLastTasks,
16
+ getStatus,
17
+ clearExport,
18
+ taskResult,
19
+ progress,
20
+ error
21
+ } = useExportService();
22
+ const {
23
+ isHidden,
24
+ hide,
25
+ show
26
+ } = useExportVisibility(showLastExportLocalStorageKey);
27
+ useEffect(() => {
28
+ if (!exportTask) {
29
+ return;
30
+ }
31
+ if (exportTask.id && (exportTask.status === 'STATUS_PENDING' || exportTask.status === 'STATUS_UNSPECIFIED')) {
32
+ clearExport();
33
+ getStatus({
34
+ uid: exportTask.id
35
+ });
36
+ show();
37
+ }
38
+ }, [clearExport, exportTask, getStatus, show]);
39
+ useEffect(() => {
40
+ if (taskResult) {
41
+ updateExportTask({
42
+ status: 'STATUS_SUCCESS',
43
+ filename: taskResult.data.file
44
+ });
45
+ }
46
+ }, [taskResult, updateExportTask]);
47
+ useEffect(() => {
48
+ if (error) {
49
+ updateExportTask({
50
+ status: 'STATUS_FAILED'
51
+ });
52
+ }
53
+ }, [error, updateExportTask]);
54
+ useEffect(() => {
55
+ if (lastTasksParams && !isHidden) {
56
+ getLastTasks(lastTasksParams).then(res => {
57
+ if (res.length > 0) {
58
+ const {
59
+ id,
60
+ result,
61
+ status,
62
+ args
63
+ } = res[0];
64
+ const {
65
+ data
66
+ } = result;
67
+ updateExportTask({
68
+ id,
69
+ status,
70
+ filename: data.file,
71
+ args,
72
+ progressTitle: 'Exporting list:',
73
+ resultSubTitle: 'Result:'
74
+ });
75
+ }
76
+ });
77
+ }
78
+ }, [getLastTasks, isHidden, lastTasksParams, updateExportTask]);
79
+ if (!exportTask || isHidden) return null;
80
+ const {
81
+ filename,
82
+ status,
83
+ resultSubTitle,
84
+ progressTitle
85
+ } = exportTask;
86
+ return React.createElement(Horizontal, {
87
+ "$padding": 8,
88
+ "$alignItems": "center",
89
+ "$border": `1px solid ${Color.DIVIDER}`,
90
+ "$borderRadius": ShapeRadius.SECTION,
91
+ "$bgColor": Color.CLEAR
92
+ }, status === 'STATUS_SUCCESS' && React.createElement(ExportResult, {
93
+ type: "success",
94
+ title: "Exporting complete",
95
+ subtitle: `${resultSubTitle} ${filename}`,
96
+ handleClose: hide,
97
+ rightSlot: React.createElement("a", {
98
+ href: `/api/v2/statistics/exportMessagesStatistics/download/${filename}`,
99
+ download: filename
100
+ }, React.createElement(Button, {
101
+ color: "secondary",
102
+ size: "compact",
103
+ icon: React.createElement(DownloadIcon, {
104
+ size: "small",
105
+ view: "filled"
106
+ })
107
+ }, "Download CSV"))
108
+ }), status === 'STATUS_PENDING' && React.createElement(ProgressBar, {
109
+ title: progressTitle,
110
+ percentValue: progress
111
+ }), status === 'STATUS_FAILED' && React.createElement(ExportResult, {
112
+ type: "error",
113
+ title: "Export failed",
114
+ subtitle: "Please try again",
115
+ leftSlotIcon: React.createElement(DangerTriangleIcon, {
116
+ size: "medium",
117
+ view: "filled"
118
+ }),
119
+ handleClose: hide
120
+ }));
121
+ };
@@ -0,0 +1,5 @@
1
+ export declare const useExportVisibility: (key: string) => {
2
+ isHidden: boolean;
3
+ hide: () => void;
4
+ show: () => void;
5
+ };
@@ -0,0 +1,17 @@
1
+ import { useCallback, useState } from 'react';
2
+ export const useExportVisibility = key => {
3
+ const [isHidden, setIsHidden] = useState(() => Boolean(+(localStorage.getItem(key) ?? 0)));
4
+ const hide = useCallback(() => {
5
+ localStorage.setItem(key, '1');
6
+ setIsHidden(true);
7
+ }, [key]);
8
+ const show = useCallback(() => {
9
+ localStorage.setItem(key, '0');
10
+ setIsHidden(false);
11
+ }, [key]);
12
+ return {
13
+ isHidden,
14
+ hide,
15
+ show
16
+ };
17
+ };
@@ -0,0 +1,2 @@
1
+ export { ExportCsvBanner } from './ExportCsvBanner';
2
+ export type { ExportTask } from './ExportCsvBanner';
@@ -0,0 +1 @@
1
+ export { ExportCsvBanner } from './ExportCsvBanner';
@@ -0,0 +1,2 @@
1
+ export * from './ExportCsvBanner';
2
+ export * from './useExportService';
@@ -0,0 +1,2 @@
1
+ export * from './ExportCsvBanner';
2
+ export * from './useExportService';
@@ -0,0 +1,2 @@
1
+ export declare const MAX_RETRIES = 180;
2
+ export declare const API_CHECK_INTERVAL = 1000;
@@ -0,0 +1,2 @@
1
+ export const MAX_RETRIES = 180;
2
+ export const API_CHECK_INTERVAL = 1000;
@@ -0,0 +1 @@
1
+ export * from './useExportService';
@@ -0,0 +1 @@
1
+ export * from './useExportService';
@@ -0,0 +1,12 @@
1
+ import type { StatusRequest, StatusResponse, ResultRequest, ResultResponse, ExportRequest, GetLastTasksRequest, TaskInfo, ResultResponse_result } from '@pushwoosh/rpc-v2-http-api-data/pushwoosh_export_messages_v2';
2
+ export declare const useExportService: () => {
3
+ getLastTasks: (params: GetLastTasksRequest) => Promise<TaskInfo[]>;
4
+ startExport: (params: ExportRequest) => Promise<number>;
5
+ getStatus: (params: StatusRequest) => Promise<StatusResponse>;
6
+ getResult: (params: ResultRequest) => Promise<ResultResponse>;
7
+ clearExport: () => void;
8
+ error: string;
9
+ progress: number;
10
+ isTaskInProgress: boolean;
11
+ taskResult: ResultResponse_result | undefined;
12
+ };
@@ -0,0 +1,119 @@
1
+ import { useCallback, useEffect, useRef, useState } from 'react';
2
+ import { API_CHECK_INTERVAL, MAX_RETRIES } from './constants';
3
+ import { useSPAContext } from '../../context';
4
+ export const useExportService = () => {
5
+ const [error, setError] = useState('');
6
+ const [progress, setProgress] = useState(0);
7
+ const [isTaskInProgress, setIsTaskInProgress] = useState(false);
8
+ const [taskResult, setTaskResult] = useState();
9
+ const timeoutStatusRef = useRef(null);
10
+ const retriesRef = useRef(0);
11
+ const {
12
+ rpcGateway
13
+ } = useSPAContext();
14
+ const clearExport = useCallback(() => {
15
+ setError('');
16
+ setIsTaskInProgress(false);
17
+ setTaskResult(undefined);
18
+ setProgress(0);
19
+ }, []);
20
+ const clearStatusTimeout = useCallback(() => {
21
+ if (timeoutStatusRef.current) {
22
+ clearTimeout(timeoutStatusRef.current);
23
+ timeoutStatusRef.current = null;
24
+ }
25
+ }, []);
26
+ const startExport = useCallback(async params => {
27
+ try {
28
+ clearExport();
29
+ setIsTaskInProgress(true);
30
+ const {
31
+ uid
32
+ } = await rpcGateway.exportStatistics.Export(params);
33
+ return uid;
34
+ } catch (err) {
35
+ setError(err === null || err === void 0 ? void 0 : err.message);
36
+ setIsTaskInProgress(false);
37
+ return 0;
38
+ }
39
+ }, [clearExport, rpcGateway.exportStatistics]);
40
+ const getResult = useCallback(async params => {
41
+ try {
42
+ const result = await rpcGateway.exportStatistics.Result(params);
43
+ setTaskResult(result.result);
44
+ return result;
45
+ } catch (err) {
46
+ setError(err === null || err === void 0 ? void 0 : err.message);
47
+ return err;
48
+ } finally {
49
+ setIsTaskInProgress(false);
50
+ }
51
+ }, [rpcGateway]);
52
+ const getStatus = useCallback(async params => {
53
+ try {
54
+ const result = await rpcGateway.exportStatistics.Status(params);
55
+ switch (result.status) {
56
+ case 'STATUS_PENDING':
57
+ if (result.progress > progress) {
58
+ setProgress(Math.round(result.progress));
59
+ }
60
+ clearStatusTimeout();
61
+ if (retriesRef.current < MAX_RETRIES) {
62
+ retriesRef.current += 1;
63
+ timeoutStatusRef.current = setTimeout(() => {
64
+ getStatus(params);
65
+ clearStatusTimeout();
66
+ }, API_CHECK_INTERVAL);
67
+ } else {
68
+ setError('Max retries reached. Please try again later.');
69
+ setIsTaskInProgress(false);
70
+ }
71
+ break;
72
+ case 'STATUS_SUCCESS':
73
+ getResult(params);
74
+ retriesRef.current = 0;
75
+ setIsTaskInProgress(false);
76
+ break;
77
+ case 'STATUS_FAILED':
78
+ setError('Error. File has not been generated. Please contact with our support team');
79
+ setIsTaskInProgress(false);
80
+ retriesRef.current = 0;
81
+ throw new Error('Export failed');
82
+ default:
83
+ break;
84
+ }
85
+ return result;
86
+ } catch (err) {
87
+ setError('Export failed. File not found');
88
+ setIsTaskInProgress(false);
89
+ retriesRef.current = 0;
90
+ return err;
91
+ }
92
+ }, [clearStatusTimeout, getResult, progress, rpcGateway]);
93
+ const getLastTasks = useCallback(async params => {
94
+ try {
95
+ const {
96
+ tasks
97
+ } = await rpcGateway.exportStatistics.GetLastTasks(params);
98
+ return tasks;
99
+ } catch (err) {
100
+ setError(err === null || err === void 0 ? void 0 : err.message);
101
+ return err;
102
+ }
103
+ }, [rpcGateway]);
104
+ useEffect(() => () => {
105
+ clearStatusTimeout();
106
+ setIsTaskInProgress(false);
107
+ }, [clearStatusTimeout]);
108
+ return {
109
+ getLastTasks,
110
+ startExport,
111
+ getStatus,
112
+ getResult,
113
+ clearExport,
114
+ error,
115
+ progress,
116
+ isTaskInProgress,
117
+ taskResult
118
+ };
119
+ };
@@ -1,7 +1,8 @@
1
1
  import { type History } from 'history';
2
2
  import type { GrpcBridge } from '@pushwoosh/grpc-bridge';
3
3
  import type { GrpcClient, HttpClient, RpcClient } from '@pushwoosh/http-client';
4
- import type { API } from '@pushwoosh/rpc-v2-http-api-data';
4
+ import type { API as RpcGatewayJourneyAPI } from '@pushwoosh/rpc-gateway-journey-data';
5
+ import type { API as RpcGatewayAPI } from '@pushwoosh/rpc-v2-http-api-data';
5
6
  import type { SessionStore } from '@pushwoosh/session-store';
6
7
  export type SPAContextType = {
7
8
  readonly history: History;
@@ -10,7 +11,8 @@ export type SPAContextType = {
10
11
  readonly grpcClient: GrpcClient;
11
12
  readonly grpcBridge: GrpcBridge;
12
13
  readonly sessionStore: SessionStore;
13
- readonly rpcGateway: API;
14
+ readonly rpcGateway: RpcGatewayAPI;
15
+ readonly rpcGatewayJourney: RpcGatewayJourneyAPI;
14
16
  readonly language: string;
15
17
  readonly helpers: {
16
18
  getApiOrigin: (name: string) => string;
package/index.d.ts CHANGED
@@ -5,3 +5,4 @@ export * from './PlatformsConfigurationDrawer';
5
5
  export * from './LanguagesSelector';
6
6
  export * from './RichMessageEditorSmart';
7
7
  export * from './CampaignsConfigurationDrawer';
8
+ export * from './ExportCSV';
package/index.js CHANGED
@@ -4,4 +4,5 @@ export * from './DynamicContentPickerForm';
4
4
  export * from './PlatformsConfigurationDrawer';
5
5
  export * from './LanguagesSelector';
6
6
  export * from './RichMessageEditorSmart';
7
- export * from './CampaignsConfigurationDrawer';
7
+ export * from './CampaignsConfigurationDrawer';
8
+ export * from './ExportCSV';
package/package.json CHANGED
@@ -43,6 +43,7 @@
43
43
  "@pushwoosh/grpc-bridge": "^1.5.36",
44
44
  "@pushwoosh/http-client": "^2.5.0",
45
45
  "@pushwoosh/rpc-v2-http-api-data": "^0.1.501",
46
+ "@pushwoosh/rpc-gateway-journey-data": "^0.26.206",
46
47
  "@pushwoosh/session-store": "^2.5.0",
47
48
  "react": "^16.14.0 || ^18.0.0",
48
49
  "react-dom": "^16.14.0 || ^18.0.0"
@@ -50,5 +51,5 @@
50
51
  "engines": {
51
52
  "node": ">= 18"
52
53
  },
53
- "version": "1.0.24"
54
+ "version": "1.0.26"
54
55
  }