@capillarytech/creatives-library 9.0.36-alpha.0 → 9.0.36-alpha.1
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/package.json +1 -1
- package/translations/en.json +2 -2
- package/utils/permissions.js +59 -0
- package/utils/tests/permissions.test.js +116 -0
- package/v2Components/AccessForbidden/_accessForbidden.scss +14 -0
- package/v2Components/AccessForbidden/index.js +5 -3
- package/v2Components/AccessForbidden/messages.js +3 -2
- package/v2Components/NoPermissionWrapper/_noPermissionWrapper.scss +12 -0
- package/v2Components/NoPermissionWrapper/index.js +26 -0
- package/v2Containers/App/constants.js +5 -0
- package/v2Containers/Assets/Gallery/_gallery.scss +6 -0
- package/v2Containers/Assets/Gallery/index.js +34 -10
- package/v2Containers/Cap/index.js +5 -2
- package/v2Containers/Cap/messages.js +4 -0
- package/v2Containers/Cap/tests/__snapshots__/index.test.js.snap +2 -2
- package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +6 -6
- package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/index.test.js.snap +4 -4
- package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +32 -32
- package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +36 -36
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +118 -118
- package/v2Containers/SmsTrai/Create/tests/__snapshots__/index.test.js.snap +8 -8
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +18 -18
- package/v2Containers/Templates/ChannelTypeIllustration.js +13 -2
- package/v2Containers/Templates/_templates.scss +101 -14
- package/v2Containers/Templates/index.js +158 -104
- package/v2Containers/Templates/tests/ChannelTypeIllustration.test.js +1 -1
- package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +174 -121
- package/v2Containers/TemplatesV2/index.js +16 -5
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +268 -268
|
@@ -98,6 +98,9 @@ import WhatsappStatusContainer from '../../v2Components/WhatsappStatusContainer'
|
|
|
98
98
|
import WechatRichmediaTemplatePreview from '../../v2Components/TemplatePreview/WechatRichmediaTemplatePreview';
|
|
99
99
|
import CapCustomSkeleton from '../../v2Components/CapCustomSkeleton';
|
|
100
100
|
import ChannelTypeIllustration from './ChannelTypeIllustration';
|
|
101
|
+
import NoPermissionWrapper from '../../v2Components/NoPermissionWrapper';
|
|
102
|
+
import { getUserAccess, hasPermission, PERMISSIONS } from '../../utils/permissions';
|
|
103
|
+
|
|
101
104
|
import TestAndPreviewSlidebox from '../../v2Components/TestAndPreviewSlidebox';
|
|
102
105
|
import creativesMessages from '../CreativesContainer/messages';
|
|
103
106
|
import {
|
|
@@ -2002,7 +2005,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
2002
2005
|
}
|
|
2003
2006
|
/>
|
|
2004
2007
|
) : (
|
|
2005
|
-
<ChannelTypeIllustration isFullMode={this.props.isFullMode} createTemplate={this.createTemplate} currentChannel={this.state?.channel?.toUpperCase()} hostName={this.state?.hostName}/>
|
|
2008
|
+
<ChannelTypeIllustration isFullMode={this.props.isFullMode} createTemplate={this.createTemplate} canCreate={this.canPerform(PERMISSIONS.CREATIVE_CREATE)} currentChannel={this.state?.channel?.toUpperCase()} hostName={this.state?.hostName}/>
|
|
2006
2009
|
);
|
|
2007
2010
|
}
|
|
2008
2011
|
|
|
@@ -2136,7 +2139,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
2136
2139
|
);
|
|
2137
2140
|
})()
|
|
2138
2141
|
],
|
|
2139
|
-
hoverOption: isArchivedMode ? null : (
|
|
2142
|
+
hoverOption: isArchivedMode || !this.canPerform(PERMISSIONS.CREATIVE_EDIT) ? null : (
|
|
2140
2143
|
<CapButton
|
|
2141
2144
|
className={
|
|
2142
2145
|
this.props.isFullMode
|
|
@@ -2200,56 +2203,47 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
2200
2203
|
{![WECHAT, WHATSAPP, ZALO].includes(
|
|
2201
2204
|
this.state.channel.toUpperCase()
|
|
2202
2205
|
) &&
|
|
2203
|
-
!isTraiDltFeature && !template.isArchived && (
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
</CapMenu.Item>
|
|
2210
|
-
)}
|
|
2206
|
+
!isTraiDltFeature && !template.isArchived && this.gatedMenuItem({
|
|
2207
|
+
allowed: this.canPerform(PERMISSIONS.CREATIVE_CREATE),
|
|
2208
|
+
className: `duplicate-${channelLowerCase}`,
|
|
2209
|
+
onClick: () => this.duplicateTemplate(template),
|
|
2210
|
+
label: <FormattedMessage {...messages.duplicateButton} />,
|
|
2211
|
+
})}
|
|
2211
2212
|
{/* Archive/Unarchive menu item (full mode only, not for Zalo, not for WhatsApp/RCS awaiting/pending) */}
|
|
2212
2213
|
{commonUtil.hasCreativesArchivalEnabled() && (() => {
|
|
2213
2214
|
const channelUp = this.state.channel.toUpperCase();
|
|
2214
2215
|
if (!this.isChannelArchiveEligible(channelUp, status, rcsStatus)) return null;
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
);
|
|
2216
|
+
const canArchive = this.canPerform(PERMISSIONS.CREATIVE_DELETE);
|
|
2217
|
+
return !template?.isArchived
|
|
2218
|
+
? this.gatedMenuItem({
|
|
2219
|
+
allowed: canArchive,
|
|
2220
|
+
className: `archive-${channelLowerCase}`,
|
|
2221
|
+
onClick: () => this.handleTemplateArchiveAction({ templateId: template._id, templateName: template.name }),
|
|
2222
|
+
label: <FormattedMessage {...messages.archiveButton} />,
|
|
2223
|
+
})
|
|
2224
|
+
: this.gatedMenuItem({
|
|
2225
|
+
allowed: canArchive,
|
|
2226
|
+
className: `unarchive-${channelLowerCase}`,
|
|
2227
|
+
onClick: () => this.handleTemplateArchiveAction({ templateId: template._id, templateName: template.name, isUnarchive: true }),
|
|
2228
|
+
label: <FormattedMessage {...messages.unarchiveButton} />,
|
|
2229
|
+
});
|
|
2230
2230
|
})()}
|
|
2231
2231
|
{/* Delete/Unmap menu item */}
|
|
2232
2232
|
{(!(
|
|
2233
2233
|
this.state.channel.toUpperCase() === WHATSAPP &&
|
|
2234
2234
|
status === WHATSAPP_STATUSES.approved
|
|
2235
|
-
) && ![ZALO].includes(this.state.channel.toUpperCase())) && (
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
<FormattedMessage {...messages.unMapButton} />
|
|
2248
|
-
) : (
|
|
2249
|
-
<FormattedMessage {...messages.deleteButton} />
|
|
2250
|
-
)}
|
|
2251
|
-
</CapMenu.Item>
|
|
2252
|
-
)}
|
|
2235
|
+
) && ![ZALO].includes(this.state.channel.toUpperCase())) && this.gatedMenuItem({
|
|
2236
|
+
allowed: this.canPerform(PERMISSIONS.CREATIVE_DELETE),
|
|
2237
|
+
className:
|
|
2238
|
+
this.state.channel.toUpperCase() === WECHAT && !template.definition
|
|
2239
|
+
? `unmap-${channelLowerCase}`
|
|
2240
|
+
: `delete-${channelLowerCase}`,
|
|
2241
|
+
onClick: () => this.toggleDeleteTemplateModal(template),
|
|
2242
|
+
label:
|
|
2243
|
+
this.state.channel.toUpperCase() === WECHAT && !template.definition
|
|
2244
|
+
? <FormattedMessage {...messages.unMapButton} />
|
|
2245
|
+
: <FormattedMessage {...messages.deleteButton} />,
|
|
2246
|
+
})}
|
|
2253
2247
|
</CapMenu>
|
|
2254
2248
|
}
|
|
2255
2249
|
>
|
|
@@ -2481,9 +2475,22 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
2481
2475
|
templateData.content = text;
|
|
2482
2476
|
if (isViberCarousel) {
|
|
2483
2477
|
const previewCards = Array.isArray(cards) ? cards.slice(0, 1) : [];
|
|
2484
|
-
|
|
2478
|
+
// Bypass CapCustomCard Viber Meta + line-clamp (clips title/buttons for tall images).
|
|
2479
|
+
// Keep VIBER in className for listing styles; cardType hits the default content path.
|
|
2480
|
+
templateData.cardType = 'VIBER_CAROUSEL_PREVIEW';
|
|
2481
|
+
templateData.className = 'viber-carousel-static VIBER';
|
|
2482
|
+
templateData.style = {
|
|
2483
|
+
height: 'auto',
|
|
2484
|
+
minHeight: '21.125rem',
|
|
2485
|
+
overflow: 'visible',
|
|
2486
|
+
};
|
|
2487
|
+
templateData.bodyStyle = {
|
|
2488
|
+
height: 'auto',
|
|
2489
|
+
overflow: 'visible',
|
|
2490
|
+
paddingBottom: '12px',
|
|
2491
|
+
};
|
|
2485
2492
|
templateData.content = (
|
|
2486
|
-
<CapRow className="viber-carousel-static-content">
|
|
2493
|
+
<CapRow vertical className="viber-carousel-static-content">
|
|
2487
2494
|
<CapRow className="viber-carousel-static-message-box">
|
|
2488
2495
|
<CapLabel type="label1" className="viber-carousel-static-message">
|
|
2489
2496
|
{text}
|
|
@@ -2491,7 +2498,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
2491
2498
|
</CapRow>
|
|
2492
2499
|
<CapRow className="viber-carousel-static-cards">
|
|
2493
2500
|
{previewCards.map((card, cardIdx) => (
|
|
2494
|
-
<CapRow className="viber-carousel-static-card" key={`viber-static-card-${cardIdx}`}>
|
|
2501
|
+
<CapRow vertical className="viber-carousel-static-card" key={`viber-static-card-${cardIdx}`}>
|
|
2495
2502
|
{card?.mediaUrl ? (
|
|
2496
2503
|
<CapImage src={card.mediaUrl} className="viber-carousel-static-image" />
|
|
2497
2504
|
) : (
|
|
@@ -2500,7 +2507,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
2500
2507
|
<CapLabel type="label1" className="viber-carousel-static-text">
|
|
2501
2508
|
{card?.text}
|
|
2502
2509
|
</CapLabel>
|
|
2503
|
-
<CapRow className="viber-carousel-static-buttons">
|
|
2510
|
+
<CapRow vertical className="viber-carousel-static-buttons">
|
|
2504
2511
|
{(Array.isArray(card?.buttons) && card.buttons.length ? card.buttons : [{}, {}]).slice(0, 2).map((carouselButton, btnIdx) => (
|
|
2505
2512
|
<CapLabel
|
|
2506
2513
|
key={`viber-static-btn-${cardIdx}-${btnIdx}`}
|
|
@@ -2820,16 +2827,18 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
2820
2827
|
<CapLabel type="label2">
|
|
2821
2828
|
{this.props.intl.formatMessage(messages.templatesSelected, { count: selectedCountLocal })}
|
|
2822
2829
|
</CapLabel>
|
|
2823
|
-
<
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
<
|
|
2831
|
-
|
|
2832
|
-
|
|
2830
|
+
<NoPermissionWrapper allowed={this.canPerform(PERMISSIONS.CREATIVE_DELETE)}>
|
|
2831
|
+
<CapButton
|
|
2832
|
+
type="primary"
|
|
2833
|
+
className="archive-template-button"
|
|
2834
|
+
prefix={<CapIcon type="archive" size="s" />}
|
|
2835
|
+
onClick={() => this.handleBulkArchiveAction({ ids: selectedIdsArrayLocal, count: selectedCountLocal, isUnarchive: isArchivedModeLocal })}
|
|
2836
|
+
>
|
|
2837
|
+
<span className="archive-btn-label">
|
|
2838
|
+
<FormattedMessage {...(isArchivedModeLocal ? messages.unarchiveButton : messages.archiveButton)} />
|
|
2839
|
+
</span>
|
|
2840
|
+
</CapButton>
|
|
2841
|
+
</NoPermissionWrapper>
|
|
2833
2842
|
<CapButton type="secondary" onClick={() => this.props.actions.clearTemplateSelection()}>
|
|
2834
2843
|
<FormattedMessage {...messages.archiveConfirmCancel} />
|
|
2835
2844
|
</CapButton>
|
|
@@ -2868,14 +2877,14 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
2868
2877
|
</div> : (
|
|
2869
2878
|
showIllustrationForChannel(MOBILE_PUSH_LOWERCASE) &&
|
|
2870
2879
|
<div style={this.isFullMode() ? { height: "calc(100vh - 325px)", overflow: 'auto'} : {}}>
|
|
2871
|
-
<ChannelTypeIllustration isFullMode={this.props.isFullMode} createTemplate={this.createTemplate} currentChannel={currentChannel} hostName={this.state?.hostName}/>
|
|
2880
|
+
<ChannelTypeIllustration isFullMode={this.props.isFullMode} createTemplate={this.createTemplate} canCreate={this.canPerform(PERMISSIONS.CREATIVE_CREATE)} currentChannel={currentChannel} hostName={this.state?.hostName}/>
|
|
2872
2881
|
</div>
|
|
2873
2882
|
)
|
|
2874
2883
|
}
|
|
2875
2884
|
{(showWhatsappIllustration || showZaloIllustration) && !get(this.props, 'Templates.isArchivedMode', false) && (
|
|
2876
2885
|
noLoaderAndSearchText &&
|
|
2877
2886
|
<div style={this.isFullMode() ? { height: "calc(100vh - 325px)", overflow: 'auto' } : {}}>
|
|
2878
|
-
{noWhatsappZaloTemplates && <ChannelTypeIllustration isFullMode={this.props.isFullMode} createTemplate={this.createTemplate} currentChannel={currentChannel} hostName={this.state?.hostName}/>}
|
|
2887
|
+
{noWhatsappZaloTemplates && <ChannelTypeIllustration isFullMode={this.props.isFullMode} createTemplate={this.createTemplate} canCreate={this.canPerform(PERMISSIONS.CREATIVE_CREATE)} currentChannel={currentChannel} hostName={this.state?.hostName}/>}
|
|
2879
2888
|
{filteredEmptyAndFullModeAs(true) && !isEmpty(templates) && this.state?.hostName && this.whatsappZaloIllustrationText(noFilteredWhatsappZaloTemplatesTitle, noFilteredWhatsappZaloTemplatesDesc)}
|
|
2880
2889
|
{noApprovedWhatsappZaloTemplates && this.whatsappZaloIllustrationText(noApprovedWhatsappZaloTemplatesTitle, showWhatsappIllustration ? noApprovedWhatsappTemplatesDesc : zaloDescIllustration)}
|
|
2881
2890
|
</div>
|
|
@@ -2883,41 +2892,41 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
2883
2892
|
}
|
|
2884
2893
|
{showIllustrationForChannel(RCS_LOWERCASE) &&
|
|
2885
2894
|
<div style={this.isFullMode() ? { height: "calc(100vh - 325px)", overflow: 'auto' } : {}}>
|
|
2886
|
-
<ChannelTypeIllustration isFullMode={this.props.isFullMode} createTemplate={this.createTemplate} currentChannel={currentChannel}/>
|
|
2895
|
+
<ChannelTypeIllustration isFullMode={this.props.isFullMode} createTemplate={this.createTemplate} canCreate={this.canPerform(PERMISSIONS.CREATIVE_CREATE)} currentChannel={currentChannel}/>
|
|
2887
2896
|
{filteredEmptyAndFullModeAs(false) && this.whatsappZaloIllustrationText(noApprovedRcsTemplatesTitle, noApprovedRcsTemplatesDesc)}
|
|
2888
2897
|
</div>
|
|
2889
2898
|
}
|
|
2890
2899
|
{showIllustrationForChannel(INAPP_LOWERCASE) &&
|
|
2891
2900
|
<div className={`${this.isFullMode() ? 'inapp-illustration-parent' : ''}`}>
|
|
2892
|
-
<ChannelTypeIllustration isFullMode={this.props.isFullMode} createTemplate={this.createTemplate} currentChannel={currentChannel} />
|
|
2901
|
+
<ChannelTypeIllustration isFullMode={this.props.isFullMode} createTemplate={this.createTemplate} canCreate={this.canPerform(PERMISSIONS.CREATIVE_CREATE)} currentChannel={currentChannel} />
|
|
2893
2902
|
</div>
|
|
2894
2903
|
}
|
|
2895
2904
|
|
|
2896
2905
|
{showIllustrationForChannel(VIBER_LOWERCASE) &&
|
|
2897
2906
|
<div className={`${this.isFullMode() ? 'viber-illustration-parent' : ''}`}>
|
|
2898
|
-
<ChannelTypeIllustration isFullMode={this.props.isFullMode} createTemplate={this.createTemplate} currentChannel={currentChannel}/>
|
|
2907
|
+
<ChannelTypeIllustration isFullMode={this.props.isFullMode} createTemplate={this.createTemplate} canCreate={this.canPerform(PERMISSIONS.CREATIVE_CREATE)} currentChannel={currentChannel}/>
|
|
2899
2908
|
</div>
|
|
2900
2909
|
}
|
|
2901
2910
|
{showIllustrationForChannel(FB_LOWERCASE) &&
|
|
2902
2911
|
<div className={`${this.isFullMode() ? 'fb-illustration-parent' : ''}`}>
|
|
2903
|
-
<ChannelTypeIllustration isFullMode={this.props.isFullMode} createTemplate={this.createTemplate} currentChannel={currentChannel}/>
|
|
2912
|
+
<ChannelTypeIllustration isFullMode={this.props.isFullMode} createTemplate={this.createTemplate} canCreate={this.canPerform(PERMISSIONS.CREATIVE_CREATE)} currentChannel={currentChannel}/>
|
|
2904
2913
|
</div>
|
|
2905
2914
|
}
|
|
2906
2915
|
{showIllustrationForChannel(LINE_LOWERCASE) &&
|
|
2907
2916
|
<div className={`${this.isFullMode() ? 'line-illustration-parent' : ''}`}>
|
|
2908
|
-
<ChannelTypeIllustration isFullMode={this.props.isFullMode} createTemplate={this.createTemplate} currentChannel={currentChannel}/>
|
|
2917
|
+
<ChannelTypeIllustration isFullMode={this.props.isFullMode} createTemplate={this.createTemplate} canCreate={this.canPerform(PERMISSIONS.CREATIVE_CREATE)} currentChannel={currentChannel}/>
|
|
2909
2918
|
</div>
|
|
2910
2919
|
}
|
|
2911
2920
|
{showIllustrationForChannel(WEBPUSH_LOWERCASE) &&
|
|
2912
2921
|
<div style={this.isFullMode() ? { height: "calc(100vh - 20.3125rem)", overflow: 'auto' } : {}}>
|
|
2913
|
-
<ChannelTypeIllustration isFullMode={this.props.isFullMode} createTemplate={this.createTemplate} currentChannel={currentChannel} hostName={this.state?.hostName}/>
|
|
2922
|
+
<ChannelTypeIllustration isFullMode={this.props.isFullMode} createTemplate={this.createTemplate} canCreate={this.canPerform(PERMISSIONS.CREATIVE_CREATE)} currentChannel={currentChannel} hostName={this.state?.hostName}/>
|
|
2914
2923
|
</div>
|
|
2915
2924
|
}
|
|
2916
2925
|
{get(this.props, 'Templates.isArchivedMode', false) && isEmpty(templates) && !isLoading && !getAllTemplatesInProgress && isEmpty(this.state.searchText) && (
|
|
2917
2926
|
<CapRow className={this.isFullMode() ? 'illustration-scroll-wrapper' : ''}>
|
|
2918
2927
|
<ChannelTypeIllustration
|
|
2919
2928
|
isFullMode={this.props.isFullMode}
|
|
2920
|
-
createTemplate={this.createTemplate}
|
|
2929
|
+
createTemplate={this.createTemplate} canCreate={this.canPerform(PERMISSIONS.CREATIVE_CREATE)}
|
|
2921
2930
|
currentChannel={currentChannel}
|
|
2922
2931
|
isArchivedMode
|
|
2923
2932
|
/>
|
|
@@ -3226,7 +3235,36 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
3226
3235
|
}
|
|
3227
3236
|
};
|
|
3228
3237
|
|
|
3238
|
+
canPerform(code) {
|
|
3239
|
+
return hasPermission(getUserAccess(this.props), code);
|
|
3240
|
+
}
|
|
3241
|
+
|
|
3242
|
+
// RBAC: render a dropdown menu item that stays visible but is grayed out,
|
|
3243
|
+
// unclickable, and shows a permission tooltip when the user lacks access
|
|
3244
|
+
// (instead of hiding it, which leaves an empty/no-op 3-dots menu).
|
|
3245
|
+
gatedMenuItem = ({ allowed, className, onClick, label }) => (
|
|
3246
|
+
<CapMenu.Item
|
|
3247
|
+
className={`${className || ''} ${allowed ? '' : 'rbac-disabled'}`.trim()}
|
|
3248
|
+
onClick={() => { if (allowed) onClick(); }}
|
|
3249
|
+
>
|
|
3250
|
+
{allowed ? label : (
|
|
3251
|
+
<CapTooltip title={<FormattedMessage {...globalMessages.noPermissionAction} />}>
|
|
3252
|
+
<span>{label}</span>
|
|
3253
|
+
</CapTooltip>
|
|
3254
|
+
)}
|
|
3255
|
+
</CapMenu.Item>
|
|
3256
|
+
);
|
|
3257
|
+
|
|
3258
|
+
guardWrite(code) {
|
|
3259
|
+
if (this.canPerform(code)) return true;
|
|
3260
|
+
CapNotification.error({
|
|
3261
|
+
message: this.props.intl.formatMessage(globalMessages.noPermissionAction),
|
|
3262
|
+
});
|
|
3263
|
+
return false;
|
|
3264
|
+
}
|
|
3265
|
+
|
|
3229
3266
|
createTemplate(ev) {
|
|
3267
|
+
if (!this.guardWrite(PERMISSIONS.CREATIVE_CREATE)) return;
|
|
3230
3268
|
const type = this.props.location.query.type;
|
|
3231
3269
|
const module = this.props.location.query.module ? this.props.location.query.module : 'default';
|
|
3232
3270
|
const channel = this.state.channel.toLowerCase();
|
|
@@ -3470,6 +3508,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
3470
3508
|
}
|
|
3471
3509
|
|
|
3472
3510
|
duplicateTemplate(template) {
|
|
3511
|
+
if (!this.guardWrite(PERMISSIONS.CREATIVE_CREATE)) return;
|
|
3473
3512
|
const duplicateObj = cloneDeep(template);
|
|
3474
3513
|
duplicateObj.name = `${COPY_OF} ${template?.name} ${moment().format('MM-DD-YYYY HH:mm:ss')}`;
|
|
3475
3514
|
delete duplicateObj._id;
|
|
@@ -3576,6 +3615,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
3576
3615
|
};
|
|
3577
3616
|
|
|
3578
3617
|
handleEditClick(e, template, modeType, path, options) {
|
|
3618
|
+
if (!this.guardWrite(PERMISSIONS.CREATIVE_EDIT)) return;
|
|
3579
3619
|
if (template && template.isArchived) {
|
|
3580
3620
|
CapNotification.error({ message: this.props.intl.formatMessage(messages.cannotEditArchivedTemplate) });
|
|
3581
3621
|
return;
|
|
@@ -3751,6 +3791,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
3751
3791
|
}
|
|
3752
3792
|
|
|
3753
3793
|
toggleDeleteTemplateModal = (template) => {
|
|
3794
|
+
if (!this.guardWrite(PERMISSIONS.CREATIVE_DELETE)) return;
|
|
3754
3795
|
this.setState({actionTemplate: template}, () => {
|
|
3755
3796
|
this.setState({showModal: true});
|
|
3756
3797
|
});
|
|
@@ -3784,6 +3825,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
3784
3825
|
}
|
|
3785
3826
|
|
|
3786
3827
|
handleBulkArchiveAction = ({ ids, count, isUnarchive = false }) => {
|
|
3828
|
+
if (!this.guardWrite(PERMISSIONS.CREATIVE_DELETE)) return;
|
|
3787
3829
|
const { intl, actions } = this.props;
|
|
3788
3830
|
const { channel } = this.state;
|
|
3789
3831
|
const title = isUnarchive
|
|
@@ -3803,6 +3845,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
3803
3845
|
};
|
|
3804
3846
|
|
|
3805
3847
|
handleTemplateArchiveAction = ({ templateId, templateName, isUnarchive = false }) => {
|
|
3848
|
+
if (!this.guardWrite(PERMISSIONS.CREATIVE_DELETE)) return;
|
|
3806
3849
|
const { intl, actions } = this.props;
|
|
3807
3850
|
const { channel } = this.state;
|
|
3808
3851
|
const title = isUnarchive
|
|
@@ -4035,35 +4078,43 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
4035
4078
|
trigger="click"
|
|
4036
4079
|
content={
|
|
4037
4080
|
<div className="popover-content">
|
|
4038
|
-
|
|
4039
|
-
<
|
|
4040
|
-
{this.
|
|
4041
|
-
|
|
4081
|
+
{this.state.channel !== 'wechat' && !template.isArchived && <div className="popover-action-container">
|
|
4082
|
+
<NoPermissionWrapper allowed={this.canPerform(PERMISSIONS.CREATIVE_CREATE)}>
|
|
4083
|
+
<CapButton type="link" onClick={() => this.duplicateTemplate(template)} className="popover-action">
|
|
4084
|
+
{this.props.intl.formatMessage(messages.duplicateButton)}
|
|
4085
|
+
</CapButton>
|
|
4086
|
+
</NoPermissionWrapper>
|
|
4042
4087
|
</div>}
|
|
4043
4088
|
{commonUtil.hasCreativesArchivalEnabled() && this.props.isFullMode && isTemplateArchiveEligible && !template.isArchived && <div className="popover-action-container">
|
|
4044
|
-
<
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
|
|
4089
|
+
<NoPermissionWrapper allowed={this.canPerform(PERMISSIONS.CREATIVE_DELETE)}>
|
|
4090
|
+
<CapButton
|
|
4091
|
+
type="link"
|
|
4092
|
+
onClick={() => this.handleTemplateArchiveAction({ templateId: template._id, templateName: template.name })}
|
|
4093
|
+
className="popover-action popover-archive-action"
|
|
4094
|
+
>
|
|
4095
|
+
<CapIcon type="archive" size="s" />
|
|
4096
|
+
<CapLabel.CapLabelInline type="label1">{this.props.intl.formatMessage(messages.archiveButton)}</CapLabel.CapLabelInline>
|
|
4097
|
+
</CapButton>
|
|
4098
|
+
</NoPermissionWrapper>
|
|
4052
4099
|
</div>}
|
|
4053
4100
|
{commonUtil.hasCreativesArchivalEnabled() && this.props.isFullMode && isTemplateArchiveEligible && template.isArchived && <div className="popover-action-container">
|
|
4054
|
-
<
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4101
|
+
<NoPermissionWrapper allowed={this.canPerform(PERMISSIONS.CREATIVE_DELETE)}>
|
|
4102
|
+
<CapButton
|
|
4103
|
+
type="link"
|
|
4104
|
+
onClick={() => this.handleTemplateArchiveAction({ templateId: template._id, templateName: template.name, isUnarchive: true })}
|
|
4105
|
+
className="popover-action popover-archive-action"
|
|
4106
|
+
>
|
|
4107
|
+
<CapIcon type="archive" size="s" />
|
|
4108
|
+
<CapLabel.CapLabelInline type="label1">{this.props.intl.formatMessage(messages.unarchiveButton)}</CapLabel.CapLabelInline>
|
|
4109
|
+
</CapButton>
|
|
4110
|
+
</NoPermissionWrapper>
|
|
4062
4111
|
</div>}
|
|
4063
4112
|
<div className="popover-action-container">
|
|
4064
|
-
<
|
|
4065
|
-
{
|
|
4066
|
-
|
|
4113
|
+
<NoPermissionWrapper allowed={this.canPerform(PERMISSIONS.CREATIVE_DELETE)}>
|
|
4114
|
+
<CapButton type="link" onClick={() => this.toggleDeleteTemplateModal(template)} className="popover-action">
|
|
4115
|
+
{deleteOption}
|
|
4116
|
+
</CapButton>
|
|
4117
|
+
</NoPermissionWrapper>
|
|
4067
4118
|
</div>
|
|
4068
4119
|
</div>
|
|
4069
4120
|
}
|
|
@@ -4665,23 +4716,26 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
4665
4716
|
&& !this.props.isFullMode
|
|
4666
4717
|
)
|
|
4667
4718
|
? (
|
|
4668
|
-
<
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4719
|
+
<NoPermissionWrapper allowed={this.canPerform(PERMISSIONS.CREATIVE_CREATE)}>
|
|
4720
|
+
<CapLink
|
|
4721
|
+
onClick={this.openCreativesFullMode}
|
|
4722
|
+
title={formatMessage(messages.createNewTemplateLink)}
|
|
4723
|
+
suffix={<CapIcon type="open-in-new" size="s" />}
|
|
4724
|
+
className="create-new-link"
|
|
4725
|
+
/>
|
|
4726
|
+
</NoPermissionWrapper>
|
|
4674
4727
|
)
|
|
4675
4728
|
: this.state?.channel?.toLowerCase() === ZALO_LOWERCASE || (channelLowerCase === WHATSAPP_LOWERCASE && isEmpty(this.state?.hostName)) ? <></> : (
|
|
4676
|
-
<
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
|
|
4684
|
-
|
|
4729
|
+
<NoPermissionWrapper allowed={this.canPerform(PERMISSIONS.CREATIVE_CREATE)}>
|
|
4730
|
+
<CapButton
|
|
4731
|
+
className={`create-new-${channelLowerCase} margin-l-8 margin-b-12`}
|
|
4732
|
+
type={"primary"}
|
|
4733
|
+
disabled={this.isCreateDisabled()}
|
|
4734
|
+
onClick={this.createTemplate}
|
|
4735
|
+
>
|
|
4736
|
+
{ this.props.intl.formatMessage((isTraiDltFeature && channel.toUpperCase() === SMS ) ? messages.uploadTemplate : messages.createNewActionButton) }
|
|
4737
|
+
</CapButton>
|
|
4738
|
+
</NoPermissionWrapper>
|
|
4685
4739
|
);
|
|
4686
4740
|
|
|
4687
4741
|
const isWhatsappCountExeeded = templatesCount >= MAX_WHATSAPP_TEMPLATES;
|
|
@@ -16,7 +16,7 @@ jest.mock('@capillarytech/cap-ui-library/assets/images/featureUiNotEnabledIllust
|
|
|
16
16
|
import React from 'react';
|
|
17
17
|
import { shallow, configure } from 'enzyme';
|
|
18
18
|
import Adapter from '@cfaester/enzyme-adapter-react-18';
|
|
19
|
-
import ChannelTypeIllustration from '../ChannelTypeIllustration';
|
|
19
|
+
import { ChannelTypeIllustration } from '../ChannelTypeIllustration';
|
|
20
20
|
import { MOBILE_PUSH, SMS, EMAIL, LINE, VIBER, FACEBOOK, WHATSAPP, RCS, ZALO, INAPP } from '../../CreativesContainer/constants';
|
|
21
21
|
|
|
22
22
|
configure({ adapter: new Adapter() });
|