@capillarytech/creatives-library 9.0.37-alpha.0 → 9.0.37
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/v2Components/AccessForbidden/index.js +3 -5
- package/v2Components/AccessForbidden/messages.js +2 -3
- package/v2Containers/App/constants.js +0 -5
- package/v2Containers/Assets/Gallery/_gallery.scss +0 -6
- package/v2Containers/Assets/Gallery/index.js +10 -34
- package/v2Containers/Cap/index.js +2 -5
- package/v2Containers/Cap/messages.js +0 -4
- 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 +1 -10
- package/v2Containers/Templates/_templates.scss +0 -6
- package/v2Containers/Templates/index.js +100 -141
- package/v2Containers/Templates/tests/ChannelTypeIllustration.test.js +0 -7
- package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +120 -173
- package/v2Containers/TemplatesV2/index.js +5 -16
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +268 -268
- package/utils/permissions.js +0 -59
- package/utils/tests/permissions.test.js +0 -116
- package/v2Components/AccessForbidden/_accessForbidden.scss +0 -14
- package/v2Components/NoPermissionWrapper/_noPermissionWrapper.scss +0 -12
- package/v2Components/NoPermissionWrapper/index.js +0 -26
package/utils/permissions.js
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
CREATIVE_VIEW,
|
|
3
|
-
CREATIVE_CREATE,
|
|
4
|
-
CREATIVE_EDIT,
|
|
5
|
-
CREATIVE_DELETE,
|
|
6
|
-
CREATIVES_UI_VIEW,
|
|
7
|
-
} from '../v2Containers/App/constants';
|
|
8
|
-
|
|
9
|
-
export const PERMISSIONS = {
|
|
10
|
-
CREATIVE_VIEW,
|
|
11
|
-
CREATIVE_CREATE,
|
|
12
|
-
CREATIVE_EDIT,
|
|
13
|
-
CREATIVE_DELETE,
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export const PERMISSION_IDS = {
|
|
17
|
-
[CREATIVE_VIEW]: 1508,
|
|
18
|
-
[CREATIVE_CREATE]: 1509,
|
|
19
|
-
[CREATIVE_EDIT]: 1510,
|
|
20
|
-
[CREATIVE_DELETE]: 1511,
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Mode-aware access resolver for the tri-modal Creatives app.
|
|
25
|
-
* Standalone: the `cap` slice is passed as a prop by the connected container.
|
|
26
|
-
* Library / MFE: the host passes a `cap` (or legacy `Global`) prop.
|
|
27
|
-
* Reads the prop first, then falls back — mirrors TemplatesV2's existing
|
|
28
|
-
* `cap.user || Global.user` resolution.
|
|
29
|
-
*/
|
|
30
|
-
export const getUserAccess = (props = {}) => {
|
|
31
|
-
const { cap = {}, Global = {} } = props;
|
|
32
|
-
const user = (cap && cap.user) || (Global && Global.user) || {};
|
|
33
|
-
const isLoaded = !!(
|
|
34
|
-
user &&
|
|
35
|
-
typeof user === 'object' &&
|
|
36
|
-
Object.keys(user).length > 0
|
|
37
|
-
);
|
|
38
|
-
return {
|
|
39
|
-
accessiblePermissions: (isLoaded && user.accessiblePermissions) || [],
|
|
40
|
-
isLoaded,
|
|
41
|
-
};
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
export const hasPermission = (userAccess, code) => {
|
|
45
|
-
if (!userAccess) return false;
|
|
46
|
-
const granted = userAccess.accessiblePermissions || [];
|
|
47
|
-
const id = PERMISSION_IDS[code];
|
|
48
|
-
return (
|
|
49
|
-
granted.includes(code) ||
|
|
50
|
-
granted.includes(id) ||
|
|
51
|
-
granted.includes(String(id))
|
|
52
|
-
);
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
// Zero-impact view gate: honour the new CREATIVE_VIEW OR the legacy CREATIVES_UI_VIEW.
|
|
56
|
-
export const canViewCreatives = userAccess =>
|
|
57
|
-
hasPermission(userAccess, CREATIVE_VIEW) ||
|
|
58
|
-
(userAccess &&
|
|
59
|
-
(userAccess.accessiblePermissions || []).includes(CREATIVES_UI_VIEW));
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Test suite for RBAC permission helpers (app/utils/permissions.js).
|
|
3
|
-
* Covers getUserAccess (loaded/unloaded, cap vs Global, missing perms),
|
|
4
|
-
* hasPermission (null guard, code/id/string-id match, empty), and
|
|
5
|
-
* canViewCreatives (new perm, legacy perm, null guard).
|
|
6
|
-
*/
|
|
7
|
-
import {
|
|
8
|
-
PERMISSIONS,
|
|
9
|
-
PERMISSION_IDS,
|
|
10
|
-
getUserAccess,
|
|
11
|
-
hasPermission,
|
|
12
|
-
canViewCreatives,
|
|
13
|
-
} from '../permissions';
|
|
14
|
-
import {
|
|
15
|
-
CREATIVE_VIEW,
|
|
16
|
-
CREATIVE_CREATE,
|
|
17
|
-
CREATIVE_EDIT,
|
|
18
|
-
CREATIVE_DELETE,
|
|
19
|
-
CREATIVES_UI_VIEW,
|
|
20
|
-
} from '../../v2Containers/App/constants';
|
|
21
|
-
|
|
22
|
-
describe('permissions - exported maps', () => {
|
|
23
|
-
it('exposes the four creative permission codes', () => {
|
|
24
|
-
expect(PERMISSIONS).toEqual({
|
|
25
|
-
CREATIVE_VIEW,
|
|
26
|
-
CREATIVE_CREATE,
|
|
27
|
-
CREATIVE_EDIT,
|
|
28
|
-
CREATIVE_DELETE,
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('maps each permission code to its id', () => {
|
|
33
|
-
expect(PERMISSION_IDS[CREATIVE_VIEW]).toBe(1508);
|
|
34
|
-
expect(PERMISSION_IDS[CREATIVE_CREATE]).toBe(1509);
|
|
35
|
-
expect(PERMISSION_IDS[CREATIVE_EDIT]).toBe(1510);
|
|
36
|
-
expect(PERMISSION_IDS[CREATIVE_DELETE]).toBe(1511);
|
|
37
|
-
});
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
describe('getUserAccess', () => {
|
|
41
|
-
it('returns unloaded access with empty perms when no props are passed', () => {
|
|
42
|
-
expect(getUserAccess()).toEqual({ accessiblePermissions: [], isLoaded: false });
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('returns unloaded access for an empty props object', () => {
|
|
46
|
-
expect(getUserAccess({})).toEqual({ accessiblePermissions: [], isLoaded: false });
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it('reads accessiblePermissions from cap.user when loaded', () => {
|
|
50
|
-
const result = getUserAccess({ cap: { user: { accessiblePermissions: [1508, 1509] } } });
|
|
51
|
-
expect(result).toEqual({ accessiblePermissions: [1508, 1509], isLoaded: true });
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('falls back to Global.user when cap.user is absent', () => {
|
|
55
|
-
const result = getUserAccess({ Global: { user: { accessiblePermissions: ['CREATIVE_VIEW'] } } });
|
|
56
|
-
expect(result).toEqual({ accessiblePermissions: ['CREATIVE_VIEW'], isLoaded: true });
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('is not loaded when the user object is empty', () => {
|
|
60
|
-
expect(getUserAccess({ cap: { user: {} } })).toEqual({ accessiblePermissions: [], isLoaded: false });
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('defaults accessiblePermissions to [] when a loaded user has none', () => {
|
|
64
|
-
const result = getUserAccess({ cap: { user: { id: 42 } } });
|
|
65
|
-
expect(result).toEqual({ accessiblePermissions: [], isLoaded: true });
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
describe('hasPermission', () => {
|
|
70
|
-
it('returns false when userAccess is null or undefined', () => {
|
|
71
|
-
expect(hasPermission(null, CREATIVE_VIEW)).toBe(false);
|
|
72
|
-
expect(hasPermission(undefined, CREATIVE_VIEW)).toBe(false);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it('returns false when accessiblePermissions is missing', () => {
|
|
76
|
-
expect(hasPermission({}, CREATIVE_VIEW)).toBe(false);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('matches by permission code', () => {
|
|
80
|
-
expect(hasPermission({ accessiblePermissions: [CREATIVE_CREATE] }, CREATIVE_CREATE)).toBe(true);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('matches by numeric permission id', () => {
|
|
84
|
-
expect(hasPermission({ accessiblePermissions: [1509] }, CREATIVE_CREATE)).toBe(true);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it('matches by string permission id', () => {
|
|
88
|
-
expect(hasPermission({ accessiblePermissions: ['1509'] }, CREATIVE_CREATE)).toBe(true);
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it('returns false when the permission is not granted', () => {
|
|
92
|
-
expect(hasPermission({ accessiblePermissions: [CREATIVE_VIEW] }, CREATIVE_DELETE)).toBe(false);
|
|
93
|
-
expect(hasPermission({ accessiblePermissions: [] }, CREATIVE_VIEW)).toBe(false);
|
|
94
|
-
});
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
describe('canViewCreatives', () => {
|
|
98
|
-
it('allows when the new CREATIVE_VIEW permission is present', () => {
|
|
99
|
-
expect(canViewCreatives({ accessiblePermissions: [CREATIVE_VIEW] })).toBe(true);
|
|
100
|
-
expect(canViewCreatives({ accessiblePermissions: [1508] })).toBe(true);
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it('allows via the legacy CREATIVES_UI_VIEW permission', () => {
|
|
104
|
-
expect(canViewCreatives({ accessiblePermissions: [CREATIVES_UI_VIEW] })).toBe(true);
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it('returns a falsy value when userAccess is null', () => {
|
|
108
|
-
// short-circuits on `userAccess &&`, so returns null (falsy) rather than false
|
|
109
|
-
expect(canViewCreatives(null)).toBeFalsy();
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
it('returns false when neither view permission is present', () => {
|
|
113
|
-
expect(canViewCreatives({ accessiblePermissions: [CREATIVE_CREATE] })).toBe(false);
|
|
114
|
-
expect(canViewCreatives({})).toBe(false);
|
|
115
|
-
});
|
|
116
|
-
});
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
// Access-denied screen: rbac-lock icon + heading + subtext (matches the shared
|
|
2
|
-
// "no access" screen used across Loyalty+ / Org Settings).
|
|
3
|
-
.no-access-icon {
|
|
4
|
-
font-size: 48px;
|
|
5
|
-
color: #8c8c8c;
|
|
6
|
-
}
|
|
7
|
-
.no-access-title {
|
|
8
|
-
margin-top: 16px;
|
|
9
|
-
}
|
|
10
|
-
.no-access-text {
|
|
11
|
-
margin-top: 8px;
|
|
12
|
-
color: #6b778c;
|
|
13
|
-
max-width: 420px;
|
|
14
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
// Dim + block interaction for a control the user lacks permission for.
|
|
2
|
-
// The wrapper keeps pointer events (so the CapTooltip still shows on hover) while the
|
|
3
|
-
// inner control is dimmed and non-interactive.
|
|
4
|
-
.rbac-no-permission {
|
|
5
|
-
display: inline-block;
|
|
6
|
-
cursor: not-allowed;
|
|
7
|
-
|
|
8
|
-
> * {
|
|
9
|
-
opacity: 0.4;
|
|
10
|
-
pointer-events: none;
|
|
11
|
-
}
|
|
12
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { FormattedMessage } from 'react-intl';
|
|
3
|
-
import CapTooltip from '@capillarytech/cap-ui-library/CapTooltip';
|
|
4
|
-
import globalMessages from '../../v2Containers/Cap/messages';
|
|
5
|
-
import './_noPermissionWrapper.scss';
|
|
6
|
-
|
|
7
|
-
// Wraps a single control and, when the user is not `allowed`, dims it + blocks
|
|
8
|
-
// interaction (via CSS) and shows a permission tooltip on hover. Uses CapTooltip
|
|
9
|
-
// instead of hand-rolled positioning/portals.
|
|
10
|
-
const NoPermissionWrapper = ({ allowed, children, message }) => {
|
|
11
|
-
const child = React.Children.only(children);
|
|
12
|
-
|
|
13
|
-
if (allowed || child.props.disabled) {
|
|
14
|
-
return child;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
return (
|
|
18
|
-
<CapTooltip
|
|
19
|
-
title={message || <FormattedMessage {...globalMessages.noPermissionAction} />}
|
|
20
|
-
>
|
|
21
|
-
<span className="rbac-no-permission">{child}</span>
|
|
22
|
-
</CapTooltip>
|
|
23
|
-
);
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
export default NoPermissionWrapper;
|