@antscorp/antsomi-ui 1.3.6-beta.77 → 1.3.6-beta.78
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/es/components/organism/PreviewCollections/WhatsappMessage/CarouselMessage/index.d.ts +1 -0
- package/es/components/organism/PreviewCollections/WhatsappMessage/CarouselMessage/index.js +11 -4
- package/es/components/organism/PreviewCollections/WhatsappMessage/HeaderMessage/index.d.ts +6 -2
- package/es/components/organism/PreviewCollections/WhatsappMessage/HeaderMessage/index.js +18 -5
- package/es/components/organism/PreviewCollections/WhatsappMessage/MediaMessage/index.js +7 -2
- package/es/components/organism/PreviewCollections/WhatsappMessage/Mobile.js +10 -5
- package/es/components/organism/PreviewCollections/WhatsappMessage/types.d.ts +3 -0
- package/es/components/organism/PreviewCollections/WhatsappMessage/utils.d.ts +1 -0
- package/es/components/organism/PreviewCollections/WhatsappMessage/utils.js +25 -0
- package/es/components/organism/PreviewCollections/constants.js +19 -0
- package/package.json +1 -1
package/es/components/organism/PreviewCollections/WhatsappMessage/CarouselMessage/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import 'swiper/swiper-bundle.css';
|
|
|
3
3
|
import type { MessageStructure } from '../types';
|
|
4
4
|
type CarouselMessageProps = Pick<MessageStructure, 'body' | 'carousel'> & {
|
|
5
5
|
messageIndex: number;
|
|
6
|
+
limitedTimeOffer: MessageStructure['limitedTimeOffer'];
|
|
6
7
|
};
|
|
7
8
|
declare const _default: React.MemoExoticComponent<(props: CarouselMessageProps) => import("react/jsx-runtime").JSX.Element>;
|
|
8
9
|
export default _default;
|
|
@@ -17,13 +17,13 @@ import FooterMessage from '../FooterMessage';
|
|
|
17
17
|
import { BASE } from '../../constants';
|
|
18
18
|
// Utils
|
|
19
19
|
import { getMediaSrc, getWhatsappComponentCls, isShowMedia } from '../utils';
|
|
20
|
+
import { globalToken } from '@antscorp/antsomi-ui/es/constants';
|
|
20
21
|
const { MARGIN_MESSAGE, PADDING_MESSAGE } = BASE.OFFSET;
|
|
21
|
-
const headerStyle = { padding: PADDING_MESSAGE, paddingBottom: 0 };
|
|
22
22
|
const bodyStyle = { padding: PADDING_MESSAGE };
|
|
23
23
|
const mediaStyle = { minWidth: BASE.LARGE.WIDTH };
|
|
24
24
|
const cls = getWhatsappComponentCls();
|
|
25
25
|
export default React.memo((props) => {
|
|
26
|
-
const { carousel, body, messageIndex } = props;
|
|
26
|
+
const { carousel, limitedTimeOffer, body, messageIndex } = props;
|
|
27
27
|
const isFirst = messageIndex === 0;
|
|
28
28
|
// Refs
|
|
29
29
|
const navigationPrevRef = useRef(null);
|
|
@@ -58,11 +58,18 @@ export default React.memo((props) => {
|
|
|
58
58
|
const { header, body, buttons } = message;
|
|
59
59
|
const isHeaderTxt = header?.format === 'TEXT';
|
|
60
60
|
const isDoc = header?.format === 'DOCUMENT';
|
|
61
|
+
const isShowHeader = isHeaderTxt || isDoc || !!limitedTimeOffer?.text;
|
|
61
62
|
return (_jsx(SwiperSlideItem, { virtualIndex: cardIdx, children: _jsx(SkeletonMessage, { size: "large", messagePadding: 0, styles: {
|
|
62
|
-
header:
|
|
63
|
+
header: {
|
|
64
|
+
padding: PADDING_MESSAGE,
|
|
65
|
+
paddingBottom: limitedTimeOffer?.text ? PADDING_MESSAGE : 0,
|
|
66
|
+
borderBottom: limitedTimeOffer?.text
|
|
67
|
+
? `1px solid ${globalToken?.bw2}`
|
|
68
|
+
: 'none',
|
|
69
|
+
},
|
|
63
70
|
body: bodyStyle,
|
|
64
71
|
media: mediaStyle,
|
|
65
|
-
}, media: isShowMedia(header?.format) ? (_jsx(MediaMessage, { src: getMediaSrc(header), format: header?.format })) : null, header:
|
|
72
|
+
}, media: isShowMedia(header?.format) ? (_jsx(MediaMessage, { src: getMediaSrc(header), format: header?.format })) : null, header: isShowHeader ? (_jsx(HeaderMessage, { header: header, limitedTimeOffer: limitedTimeOffer })) : null, body: _jsx(BodyMessage, { ...body }), footer: _jsx(FooterMessage, { buttons: buttons }) }) }, cardIdx));
|
|
66
73
|
}) })] }));
|
|
67
74
|
}, []);
|
|
68
75
|
return (_jsxs("div", { children: [_jsx("div", { style: {
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import type { MessageHeader } from '../types';
|
|
3
|
-
|
|
2
|
+
import type { MessageHeader, MessageStructure } from '../types';
|
|
3
|
+
type MessageHeaderProps = {
|
|
4
|
+
header: MessageHeader;
|
|
5
|
+
limitedTimeOffer: MessageStructure['limitedTimeOffer'];
|
|
6
|
+
};
|
|
7
|
+
declare const _default: React.MemoExoticComponent<(props: MessageHeaderProps) => import("react/jsx-runtime").JSX.Element | null>;
|
|
4
8
|
export default _default;
|
|
@@ -5,19 +5,32 @@ import { memo } from 'react';
|
|
|
5
5
|
import PDFImage from '@antscorp/antsomi-ui/es/assets/images/previews/pdf.png';
|
|
6
6
|
// Components
|
|
7
7
|
import { BaseParagraphMessage } from '../../styled';
|
|
8
|
-
import { Flex } from 'antd';
|
|
8
|
+
import { Flex, Typography } from 'antd';
|
|
9
|
+
import { RedeemIcon } from '@antscorp/antsomi-ui/es/components/icons';
|
|
9
10
|
// Utils
|
|
10
|
-
import { getWhatsappComponentCls } from '../utils';
|
|
11
|
+
import { formatEndDateTime, getWhatsappComponentCls } from '../utils';
|
|
12
|
+
import { globalToken } from '@antscorp/antsomi-ui/es/constants';
|
|
11
13
|
const cls = getWhatsappComponentCls();
|
|
12
14
|
export default memo((props) => {
|
|
13
|
-
const {
|
|
15
|
+
const { header, limitedTimeOffer } = props;
|
|
16
|
+
const { format } = header;
|
|
17
|
+
if (limitedTimeOffer?.text) {
|
|
18
|
+
const { text, startDate, startTime, code } = limitedTimeOffer;
|
|
19
|
+
return (_jsxs(Flex, { gap: 10, align: "center", style: { height: 60 }, children: [_jsx(Flex, { justify: "center", align: "center", style: {
|
|
20
|
+
background: '#EEF5FC',
|
|
21
|
+
width: 40,
|
|
22
|
+
height: 40,
|
|
23
|
+
borderRadius: '50px',
|
|
24
|
+
flexShrink: 0,
|
|
25
|
+
}, children: _jsx(RedeemIcon, { size: 30, style: { fill: globalToken?.colorPrimary } }) }), _jsxs("div", { style: { maxWidth: 'calc(100% - 50px)' }, children: [_jsx(Typography.Text, { ellipsis: { tooltip: true }, style: { fontWeight: 800, display: 'block' }, children: text }), _jsx(Typography.Text, { ellipsis: { tooltip: true }, style: { fontSize: '11px', color: globalToken?.bw7, display: 'block' }, children: formatEndDateTime(startDate, startTime) }), _jsxs(Typography.Text, { ellipsis: { tooltip: true }, style: { fontSize: '11px', color: globalToken?.bw6, display: 'block' }, children: ["Code: ", code || 'BUY2GET1'] })] })] }));
|
|
26
|
+
}
|
|
14
27
|
switch (format) {
|
|
15
28
|
case 'TEXT': {
|
|
16
|
-
const { text } =
|
|
29
|
+
const { text } = header;
|
|
17
30
|
return (_jsx(BaseParagraphMessage, { className: `${cls('header')} base-paragraph-message`, style: { fontWeight: 800 }, ellipsis: { tooltip: true, rows: 3 }, children: text }));
|
|
18
31
|
}
|
|
19
32
|
case 'DOCUMENT': {
|
|
20
|
-
const { documentName } =
|
|
33
|
+
const { documentName } = header;
|
|
21
34
|
return (_jsxs(Flex, { align: "center", gap: 4, className: `${cls('header')}`, style: { minHeight: 48, padding: '0px 5px' }, children: [_jsx("img", { src: PDFImage, alt: "pdf icon", width: 20, height: 20, style: { objectFit: 'contain' } }), _jsx(BaseParagraphMessage, { className: "base-paragraph-message", ellipsis: { rows: 2, tooltip: true }, children: documentName || 'Document Name' })] }));
|
|
22
35
|
}
|
|
23
36
|
default:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
/* eslint-disable jsx-a11y/media-has-caption */
|
|
3
3
|
// Libraries
|
|
4
|
-
import { memo, useMemo, useState } from 'react';
|
|
4
|
+
import { memo, useEffect, useMemo, useState } from 'react';
|
|
5
5
|
// Assets
|
|
6
6
|
import AntLogo from '@antscorp/antsomi-ui/es/assets/images/logo/antsomi_logo.png';
|
|
7
7
|
import MediaUnavailable from '@antscorp/antsomi-ui/es/assets/images/previews/media-unavailable.png';
|
|
@@ -29,6 +29,11 @@ export default memo((props) => {
|
|
|
29
29
|
objectFit: objFit,
|
|
30
30
|
};
|
|
31
31
|
}, [format, src]);
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (src && format === 'VIDEO') {
|
|
34
|
+
setIsVideoFails(false);
|
|
35
|
+
}
|
|
36
|
+
}, [src, format]);
|
|
32
37
|
if (format === 'VIDEO') {
|
|
33
38
|
if (!src || isVideoFails) {
|
|
34
39
|
return (_jsx(Image, { src: isVideoFails ? MediaUnavailable : AntLogo, alt: "media message header", width: "100%", height: BASE.MEDIA.MAX_HEIGHT, style: {
|
|
@@ -39,7 +44,7 @@ export default memo((props) => {
|
|
|
39
44
|
}
|
|
40
45
|
return (_jsxs("video", { width: "100%", style: { height: BASE.MEDIA.MAX_HEIGHT }, controls: true, autoPlay: false, onError: () => {
|
|
41
46
|
setIsVideoFails(true);
|
|
42
|
-
}, children: [_jsx("source", { src: src, type: "video/mp4" }), "Your browser does not support the video tag."] }));
|
|
47
|
+
}, children: [_jsx("source", { src: src, type: "video/mp4" }), "Your browser does not support the video tag."] }, src));
|
|
43
48
|
}
|
|
44
49
|
return (_jsx(Image, { src: src || AntLogo, alt: "media message header", width: "100%", height: BASE.MEDIA.MAX_HEIGHT, style: {
|
|
45
50
|
objectFit,
|
|
@@ -18,9 +18,9 @@ import CarouselMessage from './CarouselMessage';
|
|
|
18
18
|
import { BASE } from '../constants';
|
|
19
19
|
// Utils
|
|
20
20
|
import { getMarginMessage, getMediaSrc, hasOnlyAttribute, isShowMedia } from './utils';
|
|
21
|
+
import { globalToken } from '@antscorp/antsomi-ui/es/constants';
|
|
21
22
|
const { OFFSET } = BASE;
|
|
22
23
|
const { PADDING_MESSAGE } = OFFSET;
|
|
23
|
-
const headerStyle = { padding: PADDING_MESSAGE, paddingBottom: 0 };
|
|
24
24
|
const bodyStyle = { padding: PADDING_MESSAGE };
|
|
25
25
|
export default memo((props) => {
|
|
26
26
|
const { branding, messages } = props;
|
|
@@ -28,13 +28,13 @@ export default memo((props) => {
|
|
|
28
28
|
const content = messageList.map((message, messageIndex) => {
|
|
29
29
|
const isFirstMessage = messageIndex === 0;
|
|
30
30
|
const isLastMessage = messageIndex === messageList.length - 1;
|
|
31
|
-
const { header, body, footer, buttons, carousel, type } = message;
|
|
31
|
+
const { header, body, footer, limitedTimeOffer, buttons, carousel, type } = message;
|
|
32
32
|
if (!type || ['TEXT', 'MEDIA'].includes(type)) {
|
|
33
33
|
const isTxt = type === 'TEXT';
|
|
34
34
|
const isMedia = type === 'MEDIA';
|
|
35
35
|
// Carousel Images
|
|
36
36
|
if (carousel) {
|
|
37
|
-
return (_jsx(CarouselMessage, { messageIndex: messageIndex, body: body, carousel: carousel }, messageIndex));
|
|
37
|
+
return (_jsx(CarouselMessage, { messageIndex: messageIndex, body: body, carousel: carousel, limitedTimeOffer: limitedTimeOffer }, messageIndex));
|
|
38
38
|
}
|
|
39
39
|
// Case text message
|
|
40
40
|
if (isTxt || hasOnlyAttribute(message, 'body')) {
|
|
@@ -45,10 +45,15 @@ export default memo((props) => {
|
|
|
45
45
|
if (isMedia) {
|
|
46
46
|
const isHeaderTxt = header?.format === 'TEXT';
|
|
47
47
|
const isDoc = header?.format === 'DOCUMENT';
|
|
48
|
+
const isShowHeader = isHeaderTxt || isDoc || !!limitedTimeOffer?.text;
|
|
48
49
|
return (_jsx(SkeletonMessage, { size: "large", messagePadding: 0, messageMargin: getMarginMessage({ isFirst: isFirstMessage, isLast: isLastMessage }), styles: {
|
|
49
|
-
header:
|
|
50
|
+
header: {
|
|
51
|
+
padding: PADDING_MESSAGE,
|
|
52
|
+
paddingBottom: limitedTimeOffer?.text ? PADDING_MESSAGE : 0,
|
|
53
|
+
borderBottom: limitedTimeOffer?.text ? `1px solid ${globalToken?.bw2}` : 'none',
|
|
54
|
+
},
|
|
50
55
|
body: bodyStyle,
|
|
51
|
-
}, media: isShowMedia(header?.format) ? (_jsx(MediaMessage, { src: getMediaSrc(header), format: header?.format })) : null, header:
|
|
56
|
+
}, media: isShowMedia(header?.format) ? (_jsx(MediaMessage, { src: getMediaSrc(header), format: header?.format })) : null, header: isShowHeader ? (_jsx(HeaderMessage, { header: header, limitedTimeOffer: limitedTimeOffer })) : null, body: _jsx(BodyMessage, { ...body }), footer: _jsx(FooterMessage, { footer: footer, buttons: buttons }) }, messageIndex));
|
|
52
57
|
}
|
|
53
58
|
}
|
|
54
59
|
return (_jsx(BaseContainerMessage, { "$margin": getMarginMessage({ isFirst: isFirstMessage, isLast: isLastMessage }), children: _jsx(Empty, { description: translate(translations._NOTI_NOT_FOUND, 'Message not found!') }) }));
|
|
@@ -9,3 +9,4 @@ export declare function hasOnlyAttribute(obj: Record<any, any>, attr: string): b
|
|
|
9
9
|
export declare const isFixedMedia: (format?: MessageHeaderDefaultImage) => boolean | undefined;
|
|
10
10
|
export declare const isShowMedia: (format?: MessageHeader['format']) => boolean | undefined;
|
|
11
11
|
export declare const getMediaSrc: (header?: MessageHeader) => any;
|
|
12
|
+
export declare function formatEndDateTime(startDate: number, startTime: number): string;
|
|
@@ -33,3 +33,28 @@ export const getMediaSrc = (header) => {
|
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
};
|
|
36
|
+
export function formatEndDateTime(startDate, startTime) {
|
|
37
|
+
// Convert timestamps to Date objects
|
|
38
|
+
const startDateObj = new Date(startDate);
|
|
39
|
+
const startTimeObj = new Date(startTime);
|
|
40
|
+
// Get current date
|
|
41
|
+
const currentDate = new Date();
|
|
42
|
+
// Function to pad single digits with zero
|
|
43
|
+
const padZero = (num) => num.toString().padStart(2, '0');
|
|
44
|
+
// Extract time from startTimeObj
|
|
45
|
+
const hours = padZero(startTimeObj.getHours());
|
|
46
|
+
const minutes = padZero(startTimeObj.getMinutes());
|
|
47
|
+
// Check if the start date is today
|
|
48
|
+
const isToday = startDateObj.getDate() === currentDate.getDate() &&
|
|
49
|
+
startDateObj.getMonth() === currentDate.getMonth() &&
|
|
50
|
+
startDateObj.getFullYear() === currentDate.getFullYear();
|
|
51
|
+
// If it's today, return "Ends today at HH:MM"
|
|
52
|
+
if (isToday) {
|
|
53
|
+
return `Ends today at ${hours}:${minutes}`;
|
|
54
|
+
}
|
|
55
|
+
// Calculate days difference
|
|
56
|
+
const timeDifference = startDateObj.getTime() - currentDate.getTime();
|
|
57
|
+
const daysDifference = Math.ceil(timeDifference / (1000 * 3600 * 24));
|
|
58
|
+
// Return "Ends in X days"
|
|
59
|
+
return `Ends in ${daysDifference} day${daysDifference !== 1 ? 's' : ''}`;
|
|
60
|
+
}
|
|
@@ -33,6 +33,25 @@ export const SAMPLE_PREVIEW = {
|
|
|
33
33
|
},
|
|
34
34
|
buttons: [{ type: 'URL', text: 'Grab the offer', url: 'https://test.com.vn' }],
|
|
35
35
|
},
|
|
36
|
+
{
|
|
37
|
+
type: 'MEDIA',
|
|
38
|
+
header: {
|
|
39
|
+
format: 'IMAGE',
|
|
40
|
+
},
|
|
41
|
+
body: {
|
|
42
|
+
text: `Hi {{1}}, the item {{2}} you were wondering about is BACK IN STOCK! 🎉
|
|
43
|
+
|
|
44
|
+
Click the button to get it!
|
|
45
|
+
`,
|
|
46
|
+
},
|
|
47
|
+
limitedTimeOffer: {
|
|
48
|
+
text: 'Buy now!',
|
|
49
|
+
startDate: new Date().getTime(),
|
|
50
|
+
startTime: new Date().getTime(),
|
|
51
|
+
code: 'testing code',
|
|
52
|
+
},
|
|
53
|
+
buttons: [{ type: 'URL', text: 'Grab the offer', url: 'https://test.com.vn' }],
|
|
54
|
+
},
|
|
36
55
|
{
|
|
37
56
|
type: 'MEDIA',
|
|
38
57
|
header: {
|