@pushwoosh/smart-components-kit 1.0.71 → 1.0.73
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/ResendModal/ResendModal.d.ts +3 -0
- package/ResendModal/ResendModal.js +180 -0
- package/ResendModal/hooks/helpers.d.ts +1 -0
- package/ResendModal/hooks/helpers.js +14 -0
- package/ResendModal/hooks/mappers.d.ts +36 -0
- package/ResendModal/hooks/mappers.js +34 -0
- package/ResendModal/hooks/useResend.d.ts +4 -0
- package/ResendModal/hooks/useResend.js +45 -0
- package/ResendModal/index.d.ts +3 -0
- package/ResendModal/index.js +2 -0
- package/ResendModal/types.d.ts +12 -0
- package/ResendModal/types.js +1 -0
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import React, { useCallback, useEffect, useMemo } from 'react';
|
|
2
|
+
import { Controller, useForm } from 'react-hook-form';
|
|
3
|
+
import { Button, FieldBox, Modal, ModalBody, ModalFooter, ModalHeader, ModalTitle, NativeInput } from '@pushwoosh/dumb-components';
|
|
4
|
+
import { Color } from '@pushwoosh/kit-constants';
|
|
5
|
+
import { Horizontal, useAsync, Vertical } from '@pushwoosh/kit-helpers';
|
|
6
|
+
import { EditIcon } from '@pushwoosh/kit-icons';
|
|
7
|
+
import { Paragraph, Text } from '@pushwoosh/kit-typography';
|
|
8
|
+
import { useSPAContext } from '@pushwoosh/spa-context';
|
|
9
|
+
import { parseMessageProperties } from './hooks/helpers';
|
|
10
|
+
import { useResend } from './hooks/useResend';
|
|
11
|
+
export const ResendModal = ({
|
|
12
|
+
isOpen,
|
|
13
|
+
onClose,
|
|
14
|
+
applicationCode,
|
|
15
|
+
message,
|
|
16
|
+
emailTemplate: providedEmailTemplate
|
|
17
|
+
}) => {
|
|
18
|
+
const {
|
|
19
|
+
history,
|
|
20
|
+
rpcGateway
|
|
21
|
+
} = useSPAContext();
|
|
22
|
+
const {
|
|
23
|
+
resendEmail
|
|
24
|
+
} = useResend();
|
|
25
|
+
const parsedProps = useMemo(() => parseMessageProperties(message.properties), [message]);
|
|
26
|
+
const emailTemplateResult = useAsync(async () => {
|
|
27
|
+
if (providedEmailTemplate) {
|
|
28
|
+
return providedEmailTemplate;
|
|
29
|
+
}
|
|
30
|
+
const presetCode = parsedProps.preset || message.preset;
|
|
31
|
+
if (!presetCode) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
const {
|
|
35
|
+
emailTemplate
|
|
36
|
+
} = await rpcGateway.emailTemplates.Get({
|
|
37
|
+
code: presetCode
|
|
38
|
+
});
|
|
39
|
+
return emailTemplate;
|
|
40
|
+
}, [providedEmailTemplate, parsedProps.preset, message.preset, rpcGateway]);
|
|
41
|
+
const effectiveEmailTemplate = providedEmailTemplate || emailTemplateResult.value;
|
|
42
|
+
const methods = useForm({
|
|
43
|
+
mode: 'onChange',
|
|
44
|
+
defaultValues: {
|
|
45
|
+
presetSubject: ''
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
const {
|
|
49
|
+
control,
|
|
50
|
+
clearErrors,
|
|
51
|
+
handleSubmit,
|
|
52
|
+
watch,
|
|
53
|
+
reset
|
|
54
|
+
} = methods;
|
|
55
|
+
const {
|
|
56
|
+
presetSubject
|
|
57
|
+
} = watch();
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
var _effectiveEmailTempla;
|
|
60
|
+
if (!isOpen) {
|
|
61
|
+
reset({
|
|
62
|
+
presetSubject: ''
|
|
63
|
+
});
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (!(effectiveEmailTemplate !== null && effectiveEmailTemplate !== void 0 && (_effectiveEmailTempla = effectiveEmailTemplate.content) !== null && _effectiveEmailTempla !== void 0 && _effectiveEmailTempla.subject)) {
|
|
67
|
+
reset({
|
|
68
|
+
presetSubject: ''
|
|
69
|
+
});
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const {
|
|
73
|
+
subject
|
|
74
|
+
} = effectiveEmailTemplate.content;
|
|
75
|
+
const subjectValue = subject.default || subject.en || Object.values(subject)[0] || '';
|
|
76
|
+
reset({
|
|
77
|
+
presetSubject: subjectValue
|
|
78
|
+
});
|
|
79
|
+
}, [isOpen, effectiveEmailTemplate, reset]);
|
|
80
|
+
const {
|
|
81
|
+
isMultilanguage
|
|
82
|
+
} = useMemo(() => {
|
|
83
|
+
var _effectiveEmailTempla2;
|
|
84
|
+
if (!(effectiveEmailTemplate !== null && effectiveEmailTemplate !== void 0 && (_effectiveEmailTempla2 = effectiveEmailTemplate.content) !== null && _effectiveEmailTempla2 !== void 0 && _effectiveEmailTempla2.subject)) {
|
|
85
|
+
return {
|
|
86
|
+
isMultilanguage: false
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
const {
|
|
90
|
+
subject
|
|
91
|
+
} = effectiveEmailTemplate.content;
|
|
92
|
+
const subjectKeys = Object.keys(subject);
|
|
93
|
+
const isMulti = subjectKeys.length > 1 || subjectKeys.length === 1 && !subject.default;
|
|
94
|
+
return {
|
|
95
|
+
isMultilanguage: isMulti
|
|
96
|
+
};
|
|
97
|
+
}, [effectiveEmailTemplate]);
|
|
98
|
+
const editMessageUrl = useMemo(() => {
|
|
99
|
+
if (!message || !applicationCode) return '';
|
|
100
|
+
const type = message.type === 'Email' ? 'email' : 'push';
|
|
101
|
+
const queryParams = new URLSearchParams({
|
|
102
|
+
type,
|
|
103
|
+
message: message.code
|
|
104
|
+
});
|
|
105
|
+
const props = message.properties || {};
|
|
106
|
+
if (props.campaignCode) queryParams.set('campaign', props.campaignCode);
|
|
107
|
+
if (props.preset) queryParams.set('preset', props.preset);
|
|
108
|
+
if (props.filter_code) queryParams.set('segment', props.filter_code);
|
|
109
|
+
if (props.rich_media_code) queryParams.set('inapp', props.rich_media_code);
|
|
110
|
+
return `/applications/${applicationCode}/one-time-messages/edit?${queryParams.toString()}`;
|
|
111
|
+
}, [applicationCode, message]);
|
|
112
|
+
const handleResend = useCallback(async () => {
|
|
113
|
+
const res = await resendEmail(applicationCode, message, presetSubject);
|
|
114
|
+
if (res) {
|
|
115
|
+
onClose();
|
|
116
|
+
}
|
|
117
|
+
}, [applicationCode, message, onClose, presetSubject, resendEmail]);
|
|
118
|
+
if (!message) return null;
|
|
119
|
+
return React.createElement(Modal, {
|
|
120
|
+
size: "medium",
|
|
121
|
+
isOpen: isOpen,
|
|
122
|
+
onClose: onClose
|
|
123
|
+
}, React.createElement(ModalHeader, null, React.createElement(ModalTitle, null, "Resend to non-openers")), React.createElement(ModalBody, null, React.createElement(Vertical, {
|
|
124
|
+
"$gap": 16
|
|
125
|
+
}, React.createElement(Paragraph, null, "This action will send a one-time email campaign to recipients who didn't open the original message. The subject above will be used. Before resending, you can edit the campaign if needed."), React.createElement(Controller, {
|
|
126
|
+
control: control,
|
|
127
|
+
name: "presetSubject",
|
|
128
|
+
rules: {
|
|
129
|
+
required: 'This field cannot be empty'
|
|
130
|
+
},
|
|
131
|
+
render: ({
|
|
132
|
+
field,
|
|
133
|
+
fieldState
|
|
134
|
+
}) => {
|
|
135
|
+
var _fieldState$error;
|
|
136
|
+
return React.createElement(FieldBox, {
|
|
137
|
+
title: "subject LINE",
|
|
138
|
+
error: !!fieldState.error && React.createElement(Text, {
|
|
139
|
+
"$color": Color.DANGER
|
|
140
|
+
}, (_fieldState$error = fieldState.error) === null || _fieldState$error === void 0 ? void 0 : _fieldState$error.message)
|
|
141
|
+
}, React.createElement(NativeInput, {
|
|
142
|
+
value: field.value ?? '',
|
|
143
|
+
disabled: isMultilanguage,
|
|
144
|
+
"$isErrored": fieldState.invalid,
|
|
145
|
+
name: field.name,
|
|
146
|
+
onChange: ({
|
|
147
|
+
target: {
|
|
148
|
+
value
|
|
149
|
+
}
|
|
150
|
+
}) => {
|
|
151
|
+
if (value) {
|
|
152
|
+
clearErrors('presetSubject');
|
|
153
|
+
}
|
|
154
|
+
field.onChange(value);
|
|
155
|
+
}
|
|
156
|
+
}));
|
|
157
|
+
}
|
|
158
|
+
}))), React.createElement(ModalFooter, null, React.createElement(Horizontal, {
|
|
159
|
+
"$justifyContent": "space-between"
|
|
160
|
+
}, React.createElement(Button, {
|
|
161
|
+
color: "secondary",
|
|
162
|
+
icon: React.createElement(EditIcon, {
|
|
163
|
+
size: "medium",
|
|
164
|
+
view: "lined"
|
|
165
|
+
}),
|
|
166
|
+
onClick: handleSubmit(() => {
|
|
167
|
+
history.push(editMessageUrl);
|
|
168
|
+
})
|
|
169
|
+
}, "Edit before resend"), React.createElement(Horizontal, {
|
|
170
|
+
"$gap": 8
|
|
171
|
+
}, React.createElement(Button, {
|
|
172
|
+
color: "secondary",
|
|
173
|
+
onClick: onClose
|
|
174
|
+
}, "Cancel"), React.createElement(Button, {
|
|
175
|
+
color: "primary",
|
|
176
|
+
onClick: handleSubmit(() => {
|
|
177
|
+
handleResend();
|
|
178
|
+
})
|
|
179
|
+
}, "Resend now")))));
|
|
180
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const parseMessageProperties: (properties: Record<string, string> | null | undefined) => Record<string, string>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const parseMessageProperties = properties => Object.entries(properties ?? {}).reduce((acc, [key, value]) => {
|
|
2
|
+
if (!value) {
|
|
3
|
+
return acc;
|
|
4
|
+
}
|
|
5
|
+
try {
|
|
6
|
+
const parsedValue = JSON.parse(value);
|
|
7
|
+
if (parsedValue && typeof parsedValue.default === 'string') {
|
|
8
|
+
acc[key] = parsedValue.default;
|
|
9
|
+
}
|
|
10
|
+
} catch {
|
|
11
|
+
console.warn(`Could not parse property "${key}"`);
|
|
12
|
+
}
|
|
13
|
+
return acc;
|
|
14
|
+
}, {});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type Message } from '@pushwoosh/rpc-v2-http-api-data/messages';
|
|
2
|
+
export interface ResendEmailRequest {
|
|
3
|
+
request: {
|
|
4
|
+
application: string;
|
|
5
|
+
notifications: Array<{
|
|
6
|
+
transactionId: string;
|
|
7
|
+
preset?: string;
|
|
8
|
+
campaign?: string;
|
|
9
|
+
subject?: string;
|
|
10
|
+
send_date: string;
|
|
11
|
+
timezone?: string;
|
|
12
|
+
devices_filter: string;
|
|
13
|
+
from: {
|
|
14
|
+
name: string;
|
|
15
|
+
email: string;
|
|
16
|
+
};
|
|
17
|
+
'reply-to'?: {
|
|
18
|
+
name: string;
|
|
19
|
+
email: string;
|
|
20
|
+
};
|
|
21
|
+
email_type: string;
|
|
22
|
+
email_category?: string;
|
|
23
|
+
use_auto_registration?: boolean;
|
|
24
|
+
created_via?: string;
|
|
25
|
+
capping_days?: number;
|
|
26
|
+
capping_count?: number;
|
|
27
|
+
capping_exclude?: boolean;
|
|
28
|
+
capping_avoid?: boolean;
|
|
29
|
+
send_rate?: number;
|
|
30
|
+
send_rate_avoid?: boolean;
|
|
31
|
+
}>;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export declare class ResendEmailPayloadMapper {
|
|
35
|
+
static getPayload(applicationCode: string, originalMessage: Message, newSubject: string): ResendEmailRequest;
|
|
36
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { v4 } from 'uuid';
|
|
2
|
+
import { parseMessageProperties } from './helpers';
|
|
3
|
+
export class ResendEmailPayloadMapper {
|
|
4
|
+
static getPayload(applicationCode, originalMessage, newSubject) {
|
|
5
|
+
const props = parseMessageProperties(originalMessage.properties);
|
|
6
|
+
const devicesFilter = `((Event("${applicationCode}", "PW_EmailSent", date daysago lte 365, count eq 1, attribute "message_code" eq "${originalMessage.code}")) \\ (Event("${applicationCode}", "PW_EmailOpen", date daysago lte 365, count eq 1, attribute "message_code" eq "${originalMessage.code}")))`;
|
|
7
|
+
return {
|
|
8
|
+
request: {
|
|
9
|
+
application: applicationCode,
|
|
10
|
+
notifications: [{
|
|
11
|
+
transactionId: v4(),
|
|
12
|
+
preset: props.preset,
|
|
13
|
+
campaign: props.campaignCode,
|
|
14
|
+
subject: newSubject,
|
|
15
|
+
send_date: 'now',
|
|
16
|
+
timezone: props.timezone,
|
|
17
|
+
devices_filter: devicesFilter,
|
|
18
|
+
from: {
|
|
19
|
+
name: props.email_from_name || '',
|
|
20
|
+
email: props.email_from_email || ''
|
|
21
|
+
},
|
|
22
|
+
'reply-to': props.email_reply_email ? {
|
|
23
|
+
name: props.email_reply_name || '',
|
|
24
|
+
email: props.email_reply_email
|
|
25
|
+
} : undefined,
|
|
26
|
+
email_type: props.email_type || 'marketing',
|
|
27
|
+
email_category: props.email_category,
|
|
28
|
+
use_auto_registration: true,
|
|
29
|
+
created_via: 'email cp'
|
|
30
|
+
}]
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
import { useToast } from '@pushwoosh/dumb-components';
|
|
3
|
+
import { useSPAContext } from '@pushwoosh/spa-context';
|
|
4
|
+
import { ResendEmailPayloadMapper } from './mappers';
|
|
5
|
+
export const useResend = () => {
|
|
6
|
+
const {
|
|
7
|
+
httpClient,
|
|
8
|
+
helpers: {
|
|
9
|
+
getJsonApiUrl
|
|
10
|
+
}
|
|
11
|
+
} = useSPAContext();
|
|
12
|
+
const {
|
|
13
|
+
openToast
|
|
14
|
+
} = useToast();
|
|
15
|
+
const resendEmail = useCallback(async (applicationCode, originalMessage, newSubject) => {
|
|
16
|
+
const payload = ResendEmailPayloadMapper.getPayload(applicationCode, originalMessage, newSubject);
|
|
17
|
+
try {
|
|
18
|
+
const res = await httpClient.post(getJsonApiUrl('createEmailMessage'), {
|
|
19
|
+
body: payload.request,
|
|
20
|
+
withAuthorization: true
|
|
21
|
+
});
|
|
22
|
+
if ((res === null || res === void 0 ? void 0 : res.status_code) !== 200) {
|
|
23
|
+
openToast({
|
|
24
|
+
title: (res === null || res === void 0 ? void 0 : res.status_message) || 'Failed to resend message',
|
|
25
|
+
type: 'error'
|
|
26
|
+
});
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
openToast({
|
|
30
|
+
title: 'Message resent successfully',
|
|
31
|
+
type: 'success'
|
|
32
|
+
});
|
|
33
|
+
return true;
|
|
34
|
+
} catch (error) {
|
|
35
|
+
openToast({
|
|
36
|
+
title: (error === null || error === void 0 ? void 0 : error.message) || 'Internal error',
|
|
37
|
+
type: 'error'
|
|
38
|
+
});
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}, [httpClient, getJsonApiUrl, openToast]);
|
|
42
|
+
return {
|
|
43
|
+
resendEmail
|
|
44
|
+
};
|
|
45
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { EmailTemplate } from '@pushwoosh/rpc-v2-http-api-data/accounts';
|
|
2
|
+
import type { Message } from '@pushwoosh/rpc-v2-http-api-data/messages';
|
|
3
|
+
export type ResendModalProps = {
|
|
4
|
+
isOpen: boolean;
|
|
5
|
+
onClose: () => void;
|
|
6
|
+
applicationCode: string;
|
|
7
|
+
message: Message;
|
|
8
|
+
emailTemplate?: EmailTemplate;
|
|
9
|
+
};
|
|
10
|
+
export interface ResendFormValues {
|
|
11
|
+
presetSubject: string;
|
|
12
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/index.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export * from '@pushwoosh/spa-context';
|
|
2
1
|
export * from './hooks';
|
|
3
2
|
export * from './CampaignsConfigurationDrawer';
|
|
4
3
|
export * from './DynamicContentPicker';
|
|
@@ -7,6 +6,7 @@ export * from './EmailTemplatePreview';
|
|
|
7
6
|
export * from './ExportCSV';
|
|
8
7
|
export * from './PlatformsConfigurationDrawer';
|
|
9
8
|
export * from './LanguagesSelector';
|
|
9
|
+
export * from './ResendModal';
|
|
10
10
|
export * from './RichMessageEditorSmart';
|
|
11
11
|
export * from './Table';
|
|
12
12
|
export * from './OpenActions';
|
package/index.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export * from '@pushwoosh/spa-context';
|
|
2
1
|
export * from './hooks';
|
|
3
2
|
export * from './CampaignsConfigurationDrawer';
|
|
4
3
|
export * from './DynamicContentPicker';
|
|
@@ -7,6 +6,7 @@ export * from './EmailTemplatePreview';
|
|
|
7
6
|
export * from './ExportCSV';
|
|
8
7
|
export * from './PlatformsConfigurationDrawer';
|
|
9
8
|
export * from './LanguagesSelector';
|
|
9
|
+
export * from './ResendModal';
|
|
10
10
|
export * from './RichMessageEditorSmart';
|
|
11
11
|
export * from './Table';
|
|
12
12
|
export * from './OpenActions';
|
package/package.json
CHANGED
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"@types/react-dom": "^18.3.0",
|
|
39
39
|
"@types/react-router-dom": "^5.3.3",
|
|
40
40
|
"@types/styled-components": "^5.1.34",
|
|
41
|
+
"@types/uuid": "^8.3.4",
|
|
41
42
|
"pre-commit": "^1.2.2",
|
|
42
43
|
"react": "^18.3.1",
|
|
43
44
|
"react-dom": "^18.3.1",
|
|
@@ -47,5 +48,5 @@
|
|
|
47
48
|
"react": "^16.14.0 || ^18.0.0",
|
|
48
49
|
"react-dom": "^16.14.0 || ^18.0.0"
|
|
49
50
|
},
|
|
50
|
-
"version": "1.0.
|
|
51
|
+
"version": "1.0.73"
|
|
51
52
|
}
|