@pushwoosh/smart-components-kit 1.0.72 → 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.
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ import { type ResendModalProps } from './types';
3
+ export declare const ResendModal: ({ isOpen, onClose, applicationCode, message, emailTemplate: providedEmailTemplate, }: ResendModalProps) => React.JSX.Element | null;
@@ -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,4 @@
1
+ import type { Message } from '@pushwoosh/rpc-v2-http-api-data/messages';
2
+ export declare const useResend: () => {
3
+ resendEmail: (applicationCode: string, originalMessage: Message, newSubject: string) => Promise<boolean>;
4
+ };
@@ -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,3 @@
1
+ export { ResendModal } from './ResendModal';
2
+ export type { ResendModalProps, ResendFormValues } from './types';
3
+ export { useResend } from './hooks/useResend';
@@ -0,0 +1,2 @@
1
+ export { ResendModal } from './ResendModal';
2
+ export { useResend } from './hooks/useResend';
@@ -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
@@ -6,6 +6,7 @@ export * from './EmailTemplatePreview';
6
6
  export * from './ExportCSV';
7
7
  export * from './PlatformsConfigurationDrawer';
8
8
  export * from './LanguagesSelector';
9
+ export * from './ResendModal';
9
10
  export * from './RichMessageEditorSmart';
10
11
  export * from './Table';
11
12
  export * from './OpenActions';
package/index.js CHANGED
@@ -6,6 +6,7 @@ export * from './EmailTemplatePreview';
6
6
  export * from './ExportCSV';
7
7
  export * from './PlatformsConfigurationDrawer';
8
8
  export * from './LanguagesSelector';
9
+ export * from './ResendModal';
9
10
  export * from './RichMessageEditorSmart';
10
11
  export * from './Table';
11
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.72"
51
+ "version": "1.0.73"
51
52
  }