@homefile/components-v2 1.0.15 → 1.1.0
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/dist/assets/locales/en/index.json +23 -1
- package/dist/components/headers/TabsHeader.js +1 -1
- package/dist/components/myProfile/MyProfileBody.d.ts +1 -1
- package/dist/components/myProfile/MyProfileBody.js +6 -2
- package/dist/components/myProfile/index.d.ts +1 -0
- package/dist/components/myProfile/index.js +1 -0
- package/dist/components/myProfile/permissions/ActionCheckbox.d.ts +2 -0
- package/dist/components/myProfile/permissions/ActionCheckbox.js +6 -0
- package/dist/components/myProfile/permissions/ActionLabel.d.ts +5 -0
- package/dist/components/myProfile/permissions/ActionLabel.js +17 -0
- package/dist/components/myProfile/permissions/PermissionsBanner.d.ts +5 -0
- package/dist/components/myProfile/permissions/PermissionsBanner.js +23 -0
- package/dist/components/myProfile/permissions/RoleButton.d.ts +2 -0
- package/dist/components/myProfile/permissions/RoleButton.js +11 -0
- package/dist/components/myProfile/permissions/RolePermissionsTab.d.ts +2 -0
- package/dist/components/myProfile/permissions/RolePermissionsTab.js +19 -0
- package/dist/components/myProfile/permissions/index.d.ts +5 -0
- package/dist/components/myProfile/permissions/index.js +5 -0
- package/dist/helpers/HomeRoles.helper.d.ts +3 -1
- package/dist/helpers/HomeRoles.helper.js +6 -1
- package/dist/helpers/myProfile/RolePermissions.helper.d.ts +2 -0
- package/dist/helpers/myProfile/RolePermissions.helper.js +72 -0
- package/dist/helpers/myProfile/index.d.ts +1 -0
- package/dist/helpers/myProfile/index.js +1 -0
- package/dist/hooks/myProfile/index.d.ts +1 -0
- package/dist/hooks/myProfile/index.js +1 -0
- package/dist/hooks/myProfile/useRolePermissionsTab.d.ts +11 -0
- package/dist/hooks/myProfile/useRolePermissionsTab.js +64 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/interfaces/myProfile/MyProfileBody.interface.d.ts +5 -4
- package/dist/interfaces/myProfile/index.d.ts +1 -0
- package/dist/interfaces/myProfile/index.js +1 -0
- package/dist/interfaces/myProfile/permissions/ActionCheckbox.interface.d.ts +8 -0
- package/dist/interfaces/myProfile/permissions/ActionCheckbox.interface.js +1 -0
- package/dist/interfaces/myProfile/permissions/RoleButton.interface.d.ts +7 -0
- package/dist/interfaces/myProfile/permissions/RoleButton.interface.js +1 -0
- package/dist/interfaces/myProfile/permissions/RolePermissionsTab.interface.d.ts +12 -0
- package/dist/interfaces/myProfile/permissions/RolePermissionsTab.interface.js +1 -0
- package/dist/interfaces/myProfile/permissions/index.d.ts +3 -0
- package/dist/interfaces/myProfile/permissions/index.js +3 -0
- package/dist/mocks/myProfile/Permissions.mock.d.ts +2 -0
- package/dist/mocks/myProfile/Permissions.mock.js +68 -0
- package/dist/mocks/myProfile/index.d.ts +1 -0
- package/dist/mocks/myProfile/index.js +1 -0
- package/dist/stories/myProfile/MyProfilePanel.stories.js +3 -4
- package/dist/stories/myProfile/permissions/RolePermissionsTab.stories.d.ts +5 -0
- package/dist/stories/myProfile/permissions/RolePermissionsTab.stories.js +16 -0
- package/package.json +1 -1
- package/src/assets/locales/en/index.json +23 -1
- package/src/components/headers/TabsHeader.tsx +1 -0
- package/src/components/myProfile/MyProfileBody.tsx +6 -1
- package/src/components/myProfile/index.ts +1 -0
- package/src/components/myProfile/permissions/ActionCheckbox.tsx +22 -0
- package/src/components/myProfile/permissions/ActionLabel.tsx +18 -0
- package/src/components/myProfile/permissions/PermissionsBanner.tsx +35 -0
- package/src/components/myProfile/permissions/RoleButton.tsx +39 -0
- package/src/components/myProfile/permissions/RolePermissionsTab.tsx +111 -0
- package/src/components/myProfile/permissions/index.ts +5 -0
- package/src/helpers/HomeRoles.helper.ts +16 -1
- package/src/helpers/myProfile/RolePermissions.helper.ts +74 -0
- package/src/helpers/myProfile/index.ts +1 -0
- package/src/hooks/myProfile/index.ts +2 -1
- package/src/hooks/myProfile/useRolePermissionsTab.ts +104 -0
- package/src/index.ts +2 -1
- package/src/interfaces/myProfile/MyProfileBody.interface.ts +5 -4
- package/src/interfaces/myProfile/index.ts +1 -0
- package/src/interfaces/myProfile/permissions/ActionCheckbox.interface.ts +9 -0
- package/src/interfaces/myProfile/permissions/RoleButton.interface.ts +8 -0
- package/src/interfaces/myProfile/permissions/RolePermissionsTab.interface.ts +19 -0
- package/src/interfaces/myProfile/permissions/index.ts +3 -0
- package/src/mocks/myProfile/Permissions.mock.ts +70 -0
- package/src/mocks/myProfile/index.ts +2 -1
- package/src/stories/myProfile/MyProfilePanel.stories.tsx +8 -8
- package/src/stories/myProfile/permissions/RolePermissionsTab.stories.tsx +23 -0
|
@@ -448,6 +448,13 @@
|
|
|
448
448
|
"title": "My Homes"
|
|
449
449
|
},
|
|
450
450
|
"myProfile": {
|
|
451
|
+
"actions": {
|
|
452
|
+
"add": "Add",
|
|
453
|
+
"can": "Can",
|
|
454
|
+
"edit": "Edit",
|
|
455
|
+
"delete": "Delete",
|
|
456
|
+
"view": "View"
|
|
457
|
+
},
|
|
451
458
|
"account": {
|
|
452
459
|
"cancel": "Cancel Account",
|
|
453
460
|
"cancelInfo": "To cancel your Homefile account, please contact Homefile below.",
|
|
@@ -477,6 +484,20 @@
|
|
|
477
484
|
"receiptsTitle": "Receipts",
|
|
478
485
|
"toDelete": "To delete a card, you must first add a new card."
|
|
479
486
|
},
|
|
487
|
+
"permissions": {
|
|
488
|
+
"contributor": {
|
|
489
|
+
"description": "Can view, add, and edit the information when you share a home.",
|
|
490
|
+
"title": "Contributor Permissions"
|
|
491
|
+
},
|
|
492
|
+
"manager": {
|
|
493
|
+
"description": "Can view, add, and edit all home information within a home.",
|
|
494
|
+
"title": "Manager Permissions"
|
|
495
|
+
},
|
|
496
|
+
"member": {
|
|
497
|
+
"description": "Can only view information when you share a home.",
|
|
498
|
+
"title": "Member Permissions"
|
|
499
|
+
}
|
|
500
|
+
},
|
|
480
501
|
"placeholders": {
|
|
481
502
|
"cardHolder": "Name on card",
|
|
482
503
|
"cardNumber": "Card number",
|
|
@@ -491,7 +512,8 @@
|
|
|
491
512
|
"tab2": "Subscription",
|
|
492
513
|
"tab3": "Payment",
|
|
493
514
|
"tab4": "Email",
|
|
494
|
-
"tab5": "Sharing"
|
|
515
|
+
"tab5": "Sharing",
|
|
516
|
+
"tab6": "Role Permissions"
|
|
495
517
|
}
|
|
496
518
|
},
|
|
497
519
|
"newPassword": {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Box, Flex, Tab, TabList, TabPanel, TabPanels, Tabs, } from '@chakra-ui/react';
|
|
3
3
|
export const TabsHeader = ({ onChange, tabList, tabIndex, defaultIndex, rightButton, }) => {
|
|
4
|
-
return (_jsxs(Tabs, { zIndex: "base", variant: "unstyled", onChange: onChange, index: tabIndex, defaultIndex: defaultIndex, children: [_jsx(Box, { bg: "lightBlue.2", children: _jsxs(Flex, { justify: "space-between", children: [_jsx(TabList, { bg: "neutral.white", w: "full", children: tabList === null || tabList === void 0 ? void 0 : tabList.map(({ label }) => (_jsx(Tab, { minW: "1rem", px: "base", py: "0", lineHeight: "10", textTransform: "uppercase", fontWeight: "medium", fontSize: "sm", fontFamily: "primary", color: "blue.3", borderBottom: "3px solid transparent", _hover: { cursor: 'pointer' }, _focus: { outline: 'none' }, _selected: {
|
|
4
|
+
return (_jsxs(Tabs, { zIndex: "base", variant: "unstyled", onChange: onChange, index: tabIndex, defaultIndex: defaultIndex, h: "100%", children: [_jsx(Box, { bg: "lightBlue.2", children: _jsxs(Flex, { justify: "space-between", children: [_jsx(TabList, { bg: "neutral.white", w: "full", children: tabList === null || tabList === void 0 ? void 0 : tabList.map(({ label }) => (_jsx(Tab, { minW: "1rem", px: "base", py: "0", lineHeight: "10", textTransform: "uppercase", fontWeight: "medium", fontSize: "sm", fontFamily: "primary", color: "blue.3", borderBottom: "3px solid transparent", _hover: { cursor: 'pointer' }, _focus: { outline: 'none' }, _selected: {
|
|
5
5
|
color: 'blue.8',
|
|
6
6
|
fontWeight: 'bold',
|
|
7
7
|
borderColor: 'blue.8',
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { MyProfileBodyI } from '../../interfaces';
|
|
2
|
-
export declare const MyProfileBody: ({ account, details, email, isLoading, payment, }: MyProfileBodyI) => import("react/jsx-runtime").JSX.Element;
|
|
2
|
+
export declare const MyProfileBody: ({ account, details, email, isLoading, payment, rolePermissions, }: MyProfileBodyI) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -3,7 +3,7 @@ import { t } from 'i18next';
|
|
|
3
3
|
import { Center, DrawerBody } from '@chakra-ui/react';
|
|
4
4
|
import { BeatLoader } from 'react-spinners';
|
|
5
5
|
import { TabsHeader } from '../headers';
|
|
6
|
-
export const MyProfileBody = ({ account, details, email, isLoading, payment, }) => {
|
|
6
|
+
export const MyProfileBody = ({ account, details, email, isLoading, payment, rolePermissions, }) => {
|
|
7
7
|
const tabs = [
|
|
8
8
|
{
|
|
9
9
|
label: t('myProfile.tabs.tab1'),
|
|
@@ -21,6 +21,10 @@ export const MyProfileBody = ({ account, details, email, isLoading, payment, })
|
|
|
21
21
|
label: t('myProfile.tabs.tab4'),
|
|
22
22
|
component: email,
|
|
23
23
|
},
|
|
24
|
-
|
|
24
|
+
{
|
|
25
|
+
label: t('myProfile.tabs.tab6'),
|
|
26
|
+
component: rolePermissions,
|
|
27
|
+
},
|
|
28
|
+
].filter((tab) => tab.component);
|
|
25
29
|
return (_jsx(DrawerBody, { p: "0", children: isLoading ? (_jsx(Center, { h: "4rem", bg: "neutral.white", children: _jsx(BeatLoader, { color: "gray", size: 8 }) })) : (_jsx(TabsHeader, { tabList: tabs })) }));
|
|
26
30
|
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Stack, Checkbox } from '@chakra-ui/react';
|
|
3
|
+
import { ActionLabel } from '../..';
|
|
4
|
+
export const ActionCheckbox = ({ action, isChecked, isDisabled, label, onChange, }) => {
|
|
5
|
+
return (_jsxs(Stack, { spacing: "1", align: "center", w: "33px", children: [label && _jsx(ActionLabel, { label: label }), _jsx(Checkbox, { isChecked: isChecked, onChange: () => onChange(action), isDisabled: isDisabled })] }));
|
|
6
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
13
|
+
import { Text } from '@chakra-ui/react';
|
|
14
|
+
export const ActionLabel = (_a) => {
|
|
15
|
+
var { label } = _a, props = __rest(_a, ["label"]);
|
|
16
|
+
return (_jsx(Text, Object.assign({ fontSize: "10px", fontWeight: "semibold", textTransform: "uppercase" }, props, { children: label })));
|
|
17
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { t } from 'i18next';
|
|
3
|
+
import { homeRoles } from '../../../helpers';
|
|
4
|
+
import { Stack, Text } from '@chakra-ui/react';
|
|
5
|
+
export const PermissionsBanner = ({ role }) => {
|
|
6
|
+
const bgColor = homeRoles[role].bg;
|
|
7
|
+
const { title, description } = textsByRole[role];
|
|
8
|
+
return (_jsxs(Stack, { spacing: "1", bg: bgColor, px: "base", py: "6", children: [_jsx(Text, { fontWeight: "semibold", color: "neutral.white", children: title }), _jsx(Text, { color: "neutral.white", children: description })] }));
|
|
9
|
+
};
|
|
10
|
+
const textsByRole = {
|
|
11
|
+
member: {
|
|
12
|
+
title: t('myProfile.permissions.member.title'),
|
|
13
|
+
description: t('myProfile.permissions.member.description'),
|
|
14
|
+
},
|
|
15
|
+
contributor: {
|
|
16
|
+
title: t('myProfile.permissions.contributor.title'),
|
|
17
|
+
description: t('myProfile.permissions.contributor.description'),
|
|
18
|
+
},
|
|
19
|
+
manager: {
|
|
20
|
+
title: t('myProfile.permissions.manager.title'),
|
|
21
|
+
description: t('myProfile.permissions.manager.description'),
|
|
22
|
+
},
|
|
23
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { homeRoles } from '../../../helpers';
|
|
3
|
+
import { Center, Text } from '@chakra-ui/react';
|
|
4
|
+
export const RoleButton = ({ role, onClick, isSelected, showBorder, }) => {
|
|
5
|
+
const bgColor = homeRoles[role].bg;
|
|
6
|
+
const borderColor = showBorder ? 'lightBlue.2' : 'transparent';
|
|
7
|
+
return (_jsx(Center, { as: "button", w: "62px", bg: isSelected ? 'lightGreen.1' : 'neutral.white', transition: "all 0.4s", _hover: {
|
|
8
|
+
bg: 'lightGreen.1',
|
|
9
|
+
cursor: 'pointer',
|
|
10
|
+
}, onClick: onClick, children: _jsx(Center, { py: "base", w: "40px", borderY: "1px solid", borderColor: borderColor, children: _jsx(Center, { bg: bgColor, borderRadius: "4px", h: "26px", w: "26px", children: _jsx(Text, { fontWeight: "semibold", color: 'neutral.white', fontSize: "md", textTransform: "uppercase", children: role.charAt(0).toUpperCase() }) }) }) }));
|
|
11
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { t } from 'i18next';
|
|
3
|
+
import { Box, Center, Flex, Stack, Text } from '@chakra-ui/react';
|
|
4
|
+
import { RoleButton, PermissionsBanner, ActionLabel, ActionCheckbox, } from '../..';
|
|
5
|
+
import { rolePermissions } from '../../../helpers';
|
|
6
|
+
import { useRolePermissionsTab } from '../../../hooks';
|
|
7
|
+
export const RolePermissionsTab = ({ selected, onSelect, }) => {
|
|
8
|
+
const { actions, callback, selectedRole, selectedActions, handleRoleChange, handleFeatureChange, handleActionChange, } = useRolePermissionsTab({ selected, onSelect });
|
|
9
|
+
return (_jsxs(Flex, { h: "100%", children: [_jsxs(Box, { bg: "neutral.white", children: [_jsx(RoleButton, { role: "member", isSelected: selectedRole === 'member', onClick: () => handleRoleChange('member'), showBorder: true }), _jsx(RoleButton, { role: "contributor", isSelected: selectedRole === 'contributor', onClick: () => handleRoleChange('contributor') }), _jsx(RoleButton, { role: "manager", isSelected: selectedRole === 'manager', onClick: () => handleRoleChange('manager'), showBorder: true })] }), _jsxs(Box, { children: [_jsx(PermissionsBanner, { role: selectedRole }), _jsx(Center, { bg: "lightBlue.6", py: "base", children: _jsxs(Flex, { w: "100%", children: [_jsxs(Flex, { flex: "1", gap: "base", justify: "flex-end", align: "start", children: [_jsx(ActionLabel, { label: t('myProfile.actions.can'), color: "neutral.white" }), _jsx(Box, { w: "1px", bg: "neutral.white", h: "100%" })] }), _jsx(Flex, { flex: "1", justify: "space-evenly", children: actions.map((action) => {
|
|
10
|
+
const disabledAllCheckbox = selectedRole === 'member' && action !== 'view';
|
|
11
|
+
return (_jsx(ActionCheckbox, { label: action, action: action, isChecked: selectedActions.includes(action), isDisabled: disabledAllCheckbox, onChange: handleActionChange }, action));
|
|
12
|
+
}) })] }) }), _jsx(Stack, { spacing: "base", py: "base", children: rolePermissions.map(({ id, label, permissions }) => {
|
|
13
|
+
return (_jsxs(Flex, { w: "100%", children: [_jsx(Flex, { flex: "1", children: _jsx(Text, { fontFamily: "secondary", ml: "base", children: label }) }), _jsx(Flex, { flex: "1", justify: "space-evenly", children: actions.map((action) => {
|
|
14
|
+
const isDisabled = !permissions[action].includes(selectedRole);
|
|
15
|
+
const isChecked = callback[selectedRole][action].includes(id);
|
|
16
|
+
return (_jsx(ActionCheckbox, { action: action, isChecked: isChecked, isDisabled: isDisabled, onChange: () => handleFeatureChange(action, id) }, action));
|
|
17
|
+
}) })] }, id));
|
|
18
|
+
}) })] })] }));
|
|
19
|
+
};
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
export
|
|
1
|
+
export type RoleType = 'guest' | 'member' | 'partner' | 'contributor' | 'manager' | 'homeowner';
|
|
2
|
+
export type ReducedRoleType = 'member' | 'contributor' | 'manager';
|
|
3
|
+
export declare const homeRoleTypes: RoleType[];
|
|
2
4
|
export declare const homeRoles: {
|
|
3
5
|
[key: string]: {
|
|
4
6
|
[key: string]: string;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
export const rolePermissions = [
|
|
2
|
+
{
|
|
3
|
+
id: 'property-data',
|
|
4
|
+
label: 'Property Data',
|
|
5
|
+
permissions: {
|
|
6
|
+
view: ['member', 'contributor', 'manager'],
|
|
7
|
+
add: ['manager'],
|
|
8
|
+
edit: ['manager'],
|
|
9
|
+
delete: ['manager'],
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
id: 'financial-data',
|
|
14
|
+
label: 'Financial Data',
|
|
15
|
+
permissions: {
|
|
16
|
+
view: ['member', 'contributor', 'manager'],
|
|
17
|
+
add: ['contributor', 'manager'],
|
|
18
|
+
edit: ['manager'],
|
|
19
|
+
delete: ['manager'],
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
id: 'partners',
|
|
24
|
+
label: 'Partners',
|
|
25
|
+
permissions: {
|
|
26
|
+
view: ['member', 'contributor', 'manager'],
|
|
27
|
+
add: ['contributor', 'manager'],
|
|
28
|
+
edit: ['manager'],
|
|
29
|
+
delete: ['manager'],
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: 'folders',
|
|
34
|
+
label: 'Folders',
|
|
35
|
+
permissions: {
|
|
36
|
+
view: ['member', 'contributor', 'manager'],
|
|
37
|
+
add: ['manager'],
|
|
38
|
+
edit: ['contributor', 'manager'],
|
|
39
|
+
delete: ['manager'],
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: 'home-items',
|
|
44
|
+
label: 'Home items',
|
|
45
|
+
permissions: {
|
|
46
|
+
view: ['member', 'contributor', 'manager'],
|
|
47
|
+
add: ['contributor', 'manager'],
|
|
48
|
+
edit: ['manager'],
|
|
49
|
+
delete: ['manager'],
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: 'rooms',
|
|
54
|
+
label: 'Rooms',
|
|
55
|
+
permissions: {
|
|
56
|
+
view: ['member', 'contributor', 'manager'],
|
|
57
|
+
add: ['contributor', 'manager'],
|
|
58
|
+
edit: ['contributor', 'manager'],
|
|
59
|
+
delete: ['manager'],
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
id: 'rooms-items',
|
|
64
|
+
label: 'Rooms items',
|
|
65
|
+
permissions: {
|
|
66
|
+
view: ['member', 'contributor', 'manager'],
|
|
67
|
+
add: ['contributor', 'manager'],
|
|
68
|
+
edit: ['contributor', 'manager'],
|
|
69
|
+
delete: ['contributor', 'manager'],
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
];
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ReducedRoleType } from '../../helpers';
|
|
2
|
+
import { ActionsPermitted, RolePermissionObject, RolePermissionsTabI } from '../../interfaces';
|
|
3
|
+
export declare const useRolePermissionsTab: ({ selected, onSelect, }: RolePermissionsTabI) => {
|
|
4
|
+
actions: ActionsPermitted[];
|
|
5
|
+
callback: RolePermissionObject;
|
|
6
|
+
selectedRole: ReducedRoleType;
|
|
7
|
+
selectedActions: ActionsPermitted[];
|
|
8
|
+
handleRoleChange: (role: ReducedRoleType) => void;
|
|
9
|
+
handleFeatureChange: (action: ActionsPermitted, feature: string) => void;
|
|
10
|
+
handleActionChange: (action: ActionsPermitted) => void;
|
|
11
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react';
|
|
2
|
+
import { rolePermissions } from '../../helpers';
|
|
3
|
+
export const useRolePermissionsTab = ({ selected, onSelect, }) => {
|
|
4
|
+
const actions = ['view', 'add', 'edit', 'delete'];
|
|
5
|
+
const selectedActionsFromSelected = () => {
|
|
6
|
+
const selectedActions = [];
|
|
7
|
+
actions.forEach((action) => {
|
|
8
|
+
const featuresByRoleByAction = selected[selectedRole][action];
|
|
9
|
+
if (featuresByRoleByAction.length === rolePermissions.length) {
|
|
10
|
+
selectedActions.push(action);
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
return selectedActions;
|
|
14
|
+
};
|
|
15
|
+
const [callback, setCallback] = useState(selected);
|
|
16
|
+
const [selectedRole, setSelectedRole] = useState('member');
|
|
17
|
+
const [selectedActions, setSelectedActions] = useState(selectedActionsFromSelected);
|
|
18
|
+
const handleRoleChange = (role) => setSelectedRole(role);
|
|
19
|
+
const checkIfHasFeature = (action, feature) => {
|
|
20
|
+
const features = callback[selectedRole][action];
|
|
21
|
+
const hasFeature = features.includes(feature);
|
|
22
|
+
if (hasFeature) {
|
|
23
|
+
return features.filter((f) => f !== feature);
|
|
24
|
+
}
|
|
25
|
+
return [...features, feature];
|
|
26
|
+
};
|
|
27
|
+
const handleFeatureChange = (action, feature) => {
|
|
28
|
+
const newCallback = Object.assign(Object.assign({}, callback), { [selectedRole]: Object.assign(Object.assign({}, callback[selectedRole]), { [action]: checkIfHasFeature(action, feature) }) });
|
|
29
|
+
setCallback(newCallback);
|
|
30
|
+
onSelect(newCallback);
|
|
31
|
+
};
|
|
32
|
+
const handleActionChange = (action) => {
|
|
33
|
+
if (selectedActions.includes(action)) {
|
|
34
|
+
const newCallback = Object.assign(Object.assign({}, callback), { [selectedRole]: Object.assign(Object.assign({}, callback[selectedRole]), { [action]: [] }) });
|
|
35
|
+
setCallback(newCallback);
|
|
36
|
+
onSelect(newCallback);
|
|
37
|
+
const filteredActions = selectedActions.filter((a) => a !== action);
|
|
38
|
+
return setSelectedActions(filteredActions);
|
|
39
|
+
}
|
|
40
|
+
const newCallback = Object.assign(Object.assign({}, callback), { [selectedRole]: Object.assign(Object.assign({}, callback[selectedRole]), { [action]: rolePermissions
|
|
41
|
+
.map(({ id, permissions }) => {
|
|
42
|
+
if (permissions[action].includes(selectedRole)) {
|
|
43
|
+
return id;
|
|
44
|
+
}
|
|
45
|
+
return '';
|
|
46
|
+
})
|
|
47
|
+
.filter((id) => id) }) });
|
|
48
|
+
setCallback(newCallback);
|
|
49
|
+
onSelect(newCallback);
|
|
50
|
+
setSelectedActions([...selectedActions, action]);
|
|
51
|
+
};
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
setSelectedActions(selectedActionsFromSelected);
|
|
54
|
+
}, [selectedRole]);
|
|
55
|
+
return {
|
|
56
|
+
actions,
|
|
57
|
+
callback,
|
|
58
|
+
selectedRole,
|
|
59
|
+
selectedActions,
|
|
60
|
+
handleRoleChange,
|
|
61
|
+
handleFeatureChange,
|
|
62
|
+
handleActionChange,
|
|
63
|
+
};
|
|
64
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { ActivateAccount, ActiveSubscription, AddCardBanner, AddFolder, AddHomeContent, AddHomeItem, AddHomeItemHeader, AddMedia, AddPropertyRecords, Address, AddPopup, AddTile, AlertBanner, AphwTile, AppBar, AssignableReceipts, BackHeader, BarDivider, CancelAccount, CatalogPopup, CloudsAnimation, ContactList, ContactsContent, ContainerHeader, CreateDocumentHeader, CreateHouseholdItemHeader, CreditCardContainer, CreditCardError, CustomerTile, DeleteBanner, DisplayFiles, DisplayFilesDetail, DisplayOptions, DisplayReceipts, DocumentMenu, DocumentNameHeader, DocumentPreview, DynamicForm, EditAccountType, EditHomeBody, EditHomeFooter, EditHomeHeader, EditHomePanel, EmailPermissions, EmailValidation, Feedback, FileDetail, FilesUploader, FlowStep, FolderDetail, FolderDetailBody, FolderDetailContent, FolderDetailFooter, FolderDetailHeader, FolderInfo, FolderSharing, FolderTypeSelection, FooterButtons, FooterDrawer, GenericBackHeader, GroupCard, GroupsContainer, GroupsHeader, Header, HelpContent, HomeAssistant, HomeAssistantPanel, HomeBoardGrid, HomeBoardTour, HomeCard, HomeCardWithRecipent, HomeHeader, HomeSharedWith, InboxTile, ItemNameHeader, ItemsReviewBanner, ItemSubTypeSelect, Launchpad, LaunchpadAutofilerBanner, LaunchpadReceiptAutofiler, LaunchpadTour, LeftPanel, MediaDetailsStep, MonthlyCharge, MortgageInfo, MoveModal, MyHomes, MyProfileContent, MyProfileFooter, MyProfilePanel, NewCreditCard, NewCreditCardHeader, NewPassword, NotBeChargedBanner, Overlay, PanelHeader, PartnerActiveSubscription, PartnerCatalogPanel, PartnerContent, PartnerCustomerCode, PartnerDetails, PartnerImages, PartnerPanel, PasswordInput, PaymentReceipts, PdfButton, PeopleConnected, ProfileDetailsTab, ProfilePaymentTab, ProjectList, PropertyRecords, PropertyTaxes, ReadOnlyDynamicForm, ReadOnlyImage, ReadOnlyToggle, ReceiptAutofiler, ReceiptBody, ReceiptContent, ReceiptDetails, ReceiptFilters, ReceiptFooter, ReceiptHeader, ReceiptInfos, ReceiptItem, ReceiptItems, ReceiptPDF, ReceiptsDisplayOptions, ReceiptsFiled, ReceiptsHeader, ReceiptsInfo, ReceiptsReceivedContent, RecipientForm, RecipientsToShare, RecipientTab, RecordsInputs, ResendResetPassword, ResetPassword, ReviewBanner, RightPanel, RoomHeader, RoomsBoardTour, RoomsMenu, RoomsMenuMobile, RoomStep, RoomVideo, SalesTax, SatisfactionTile, SectionHeader, SendCommunication, SharedAccounts, SharedHomeContent, SharedHomeHeader, ShareHome, ShareHomeConnections, ShareHomeForm, ShortPartnerTile, SignIn, SignUp, StepHeader, StorageUsed, Subscription, SubscriptionCard, SubscriptionTable, Summary, TabsHeader, TextInput, ToBeDeletedBody, ToBeDeletedContent, ToBeDeletedFooter, ToBeDeletedHeader, TourButton, Trash, TrendingValue, TrialBanner, TutorialsButton, TwoFactor, TwoFactorSetting, UpdateList, UserDetails, ValueMonitor, VideoPlayer, VideoPlayerModal, WellDone, YtdTile, } from './components';
|
|
1
|
+
export { ActivateAccount, ActiveSubscription, AddCardBanner, AddFolder, AddHomeContent, AddHomeItem, AddHomeItemHeader, AddMedia, AddPropertyRecords, Address, AddPopup, AddTile, AlertBanner, AphwTile, AppBar, AssignableReceipts, BackHeader, BarDivider, CancelAccount, CatalogPopup, CloudsAnimation, ContactList, ContactsContent, ContainerHeader, CreateDocumentHeader, CreateHouseholdItemHeader, CreditCardContainer, CreditCardError, CustomerTile, DeleteBanner, DisplayFiles, DisplayFilesDetail, DisplayOptions, DisplayReceipts, DocumentMenu, DocumentNameHeader, DocumentPreview, DynamicForm, EditAccountType, EditHomeBody, EditHomeFooter, EditHomeHeader, EditHomePanel, EmailPermissions, EmailValidation, Feedback, FileDetail, FilesUploader, FlowStep, FolderDetail, FolderDetailBody, FolderDetailContent, FolderDetailFooter, FolderDetailHeader, FolderInfo, FolderSharing, FolderTypeSelection, FooterButtons, FooterDrawer, GenericBackHeader, GroupCard, GroupsContainer, GroupsHeader, Header, HelpContent, HomeAssistant, HomeAssistantPanel, HomeBoardGrid, HomeBoardTour, HomeCard, HomeCardWithRecipent, HomeHeader, HomeSharedWith, InboxTile, ItemNameHeader, ItemsReviewBanner, ItemSubTypeSelect, Launchpad, LaunchpadAutofilerBanner, LaunchpadReceiptAutofiler, LaunchpadTour, LeftPanel, MediaDetailsStep, MonthlyCharge, MortgageInfo, MoveModal, MyHomes, MyProfileContent, MyProfileFooter, MyProfilePanel, NewCreditCard, NewCreditCardHeader, NewPassword, NotBeChargedBanner, Overlay, PanelHeader, PartnerActiveSubscription, PartnerCatalogPanel, PartnerContent, PartnerCustomerCode, PartnerDetails, PartnerImages, PartnerPanel, PasswordInput, PaymentReceipts, PdfButton, PeopleConnected, ProfileDetailsTab, ProfilePaymentTab, ProjectList, PropertyRecords, PropertyTaxes, ReadOnlyDynamicForm, ReadOnlyImage, ReadOnlyToggle, ReceiptAutofiler, ReceiptBody, ReceiptContent, ReceiptDetails, ReceiptFilters, ReceiptFooter, ReceiptHeader, ReceiptInfos, ReceiptItem, ReceiptItems, ReceiptPDF, ReceiptsDisplayOptions, ReceiptsFiled, ReceiptsHeader, ReceiptsInfo, ReceiptsReceivedContent, RecipientForm, RecipientsToShare, RecipientTab, RecordsInputs, ResendResetPassword, ResetPassword, ReviewBanner, RightPanel, RolePermissionsTab, RoomHeader, RoomsBoardTour, RoomsMenu, RoomsMenuMobile, RoomStep, RoomVideo, SalesTax, SatisfactionTile, SectionHeader, SendCommunication, SharedAccounts, SharedHomeContent, SharedHomeHeader, ShareHome, ShareHomeConnections, ShareHomeForm, ShortPartnerTile, SignIn, SignUp, StepHeader, StorageUsed, Subscription, SubscriptionCard, SubscriptionTable, Summary, TabsHeader, TextInput, ToBeDeletedBody, ToBeDeletedContent, ToBeDeletedFooter, ToBeDeletedHeader, TourButton, Trash, TrendingValue, TrialBanner, TutorialsButton, TwoFactor, TwoFactorSetting, UpdateList, UserDetails, ValueMonitor, VideoPlayer, VideoPlayerModal, WellDone, YtdTile, } from './components';
|
|
2
2
|
export { useCustomToast } from './hooks';
|
|
3
|
-
export { billingProxy, dynamicFormProxy, homeCardProxy, partnerContentProxy, partnerDetailsProxy, paymentMethodProxy, recordsInputsProxy, recordsInputsToDBProxy, userDetailsProxy, confirmAddressProxy } from './proxies';
|
|
3
|
+
export { billingProxy, dynamicFormProxy, homeCardProxy, partnerContentProxy, partnerDetailsProxy, paymentMethodProxy, recordsInputsProxy, recordsInputsToDBProxy, userDetailsProxy, confirmAddressProxy, } from './proxies';
|
|
4
4
|
import theme from './theme';
|
|
5
5
|
export { theme };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { ActivateAccount, ActiveSubscription, AddCardBanner, AddFolder, AddHomeContent, AddHomeItem, AddHomeItemHeader, AddMedia, AddPropertyRecords, Address, AddPopup, AddTile, AlertBanner, AphwTile, AppBar, AssignableReceipts, BackHeader, BarDivider, CancelAccount, CatalogPopup, CloudsAnimation, ContactList, ContactsContent, ContainerHeader, CreateDocumentHeader, CreateHouseholdItemHeader, CreditCardContainer, CreditCardError, CustomerTile, DeleteBanner, DisplayFiles, DisplayFilesDetail, DisplayOptions, DisplayReceipts, DocumentMenu, DocumentNameHeader, DocumentPreview, DynamicForm, EditAccountType, EditHomeBody, EditHomeFooter, EditHomeHeader, EditHomePanel, EmailPermissions, EmailValidation, Feedback, FileDetail, FilesUploader, FlowStep, FolderDetail, FolderDetailBody, FolderDetailContent, FolderDetailFooter, FolderDetailHeader, FolderInfo, FolderSharing, FolderTypeSelection, FooterButtons, FooterDrawer, GenericBackHeader, GroupCard, GroupsContainer, GroupsHeader, Header, HelpContent, HomeAssistant, HomeAssistantPanel, HomeBoardGrid, HomeBoardTour, HomeCard, HomeCardWithRecipent, HomeHeader, HomeSharedWith, InboxTile, ItemNameHeader, ItemsReviewBanner, ItemSubTypeSelect, Launchpad, LaunchpadAutofilerBanner, LaunchpadReceiptAutofiler, LaunchpadTour, LeftPanel, MediaDetailsStep, MonthlyCharge, MortgageInfo, MoveModal, MyHomes, MyProfileContent, MyProfileFooter, MyProfilePanel, NewCreditCard, NewCreditCardHeader, NewPassword, NotBeChargedBanner, Overlay, PanelHeader, PartnerActiveSubscription, PartnerCatalogPanel, PartnerContent, PartnerCustomerCode, PartnerDetails, PartnerImages, PartnerPanel, PasswordInput, PaymentReceipts, PdfButton, PeopleConnected, ProfileDetailsTab, ProfilePaymentTab, ProjectList, PropertyRecords, PropertyTaxes, ReadOnlyDynamicForm, ReadOnlyImage, ReadOnlyToggle, ReceiptAutofiler, ReceiptBody, ReceiptContent, ReceiptDetails, ReceiptFilters, ReceiptFooter, ReceiptHeader, ReceiptInfos, ReceiptItem, ReceiptItems, ReceiptPDF, ReceiptsDisplayOptions, ReceiptsFiled, ReceiptsHeader, ReceiptsInfo, ReceiptsReceivedContent, RecipientForm, RecipientsToShare, RecipientTab, RecordsInputs, ResendResetPassword, ResetPassword, ReviewBanner, RightPanel, RoomHeader, RoomsBoardTour, RoomsMenu, RoomsMenuMobile, RoomStep, RoomVideo, SalesTax, SatisfactionTile, SectionHeader, SendCommunication, SharedAccounts, SharedHomeContent, SharedHomeHeader, ShareHome, ShareHomeConnections, ShareHomeForm, ShortPartnerTile, SignIn, SignUp, StepHeader, StorageUsed, Subscription, SubscriptionCard, SubscriptionTable, Summary, TabsHeader, TextInput, ToBeDeletedBody, ToBeDeletedContent, ToBeDeletedFooter, ToBeDeletedHeader, TourButton, Trash, TrendingValue, TrialBanner, TutorialsButton, TwoFactor, TwoFactorSetting, UpdateList, UserDetails, ValueMonitor, VideoPlayer, VideoPlayerModal, WellDone, YtdTile, } from './components';
|
|
1
|
+
export { ActivateAccount, ActiveSubscription, AddCardBanner, AddFolder, AddHomeContent, AddHomeItem, AddHomeItemHeader, AddMedia, AddPropertyRecords, Address, AddPopup, AddTile, AlertBanner, AphwTile, AppBar, AssignableReceipts, BackHeader, BarDivider, CancelAccount, CatalogPopup, CloudsAnimation, ContactList, ContactsContent, ContainerHeader, CreateDocumentHeader, CreateHouseholdItemHeader, CreditCardContainer, CreditCardError, CustomerTile, DeleteBanner, DisplayFiles, DisplayFilesDetail, DisplayOptions, DisplayReceipts, DocumentMenu, DocumentNameHeader, DocumentPreview, DynamicForm, EditAccountType, EditHomeBody, EditHomeFooter, EditHomeHeader, EditHomePanel, EmailPermissions, EmailValidation, Feedback, FileDetail, FilesUploader, FlowStep, FolderDetail, FolderDetailBody, FolderDetailContent, FolderDetailFooter, FolderDetailHeader, FolderInfo, FolderSharing, FolderTypeSelection, FooterButtons, FooterDrawer, GenericBackHeader, GroupCard, GroupsContainer, GroupsHeader, Header, HelpContent, HomeAssistant, HomeAssistantPanel, HomeBoardGrid, HomeBoardTour, HomeCard, HomeCardWithRecipent, HomeHeader, HomeSharedWith, InboxTile, ItemNameHeader, ItemsReviewBanner, ItemSubTypeSelect, Launchpad, LaunchpadAutofilerBanner, LaunchpadReceiptAutofiler, LaunchpadTour, LeftPanel, MediaDetailsStep, MonthlyCharge, MortgageInfo, MoveModal, MyHomes, MyProfileContent, MyProfileFooter, MyProfilePanel, NewCreditCard, NewCreditCardHeader, NewPassword, NotBeChargedBanner, Overlay, PanelHeader, PartnerActiveSubscription, PartnerCatalogPanel, PartnerContent, PartnerCustomerCode, PartnerDetails, PartnerImages, PartnerPanel, PasswordInput, PaymentReceipts, PdfButton, PeopleConnected, ProfileDetailsTab, ProfilePaymentTab, ProjectList, PropertyRecords, PropertyTaxes, ReadOnlyDynamicForm, ReadOnlyImage, ReadOnlyToggle, ReceiptAutofiler, ReceiptBody, ReceiptContent, ReceiptDetails, ReceiptFilters, ReceiptFooter, ReceiptHeader, ReceiptInfos, ReceiptItem, ReceiptItems, ReceiptPDF, ReceiptsDisplayOptions, ReceiptsFiled, ReceiptsHeader, ReceiptsInfo, ReceiptsReceivedContent, RecipientForm, RecipientsToShare, RecipientTab, RecordsInputs, ResendResetPassword, ResetPassword, ReviewBanner, RightPanel, RolePermissionsTab, RoomHeader, RoomsBoardTour, RoomsMenu, RoomsMenuMobile, RoomStep, RoomVideo, SalesTax, SatisfactionTile, SectionHeader, SendCommunication, SharedAccounts, SharedHomeContent, SharedHomeHeader, ShareHome, ShareHomeConnections, ShareHomeForm, ShortPartnerTile, SignIn, SignUp, StepHeader, StorageUsed, Subscription, SubscriptionCard, SubscriptionTable, Summary, TabsHeader, TextInput, ToBeDeletedBody, ToBeDeletedContent, ToBeDeletedFooter, ToBeDeletedHeader, TourButton, Trash, TrendingValue, TrialBanner, TutorialsButton, TwoFactor, TwoFactorSetting, UpdateList, UserDetails, ValueMonitor, VideoPlayer, VideoPlayerModal, WellDone, YtdTile, } from './components';
|
|
2
2
|
export { useCustomToast } from './hooks';
|
|
3
|
-
export { billingProxy, dynamicFormProxy, homeCardProxy, partnerContentProxy, partnerDetailsProxy, paymentMethodProxy, recordsInputsProxy, recordsInputsToDBProxy, userDetailsProxy, confirmAddressProxy } from './proxies';
|
|
3
|
+
export { billingProxy, dynamicFormProxy, homeCardProxy, partnerContentProxy, partnerDetailsProxy, paymentMethodProxy, recordsInputsProxy, recordsInputsToDBProxy, userDetailsProxy, confirmAddressProxy, } from './proxies';
|
|
4
4
|
import theme from './theme';
|
|
5
5
|
export { theme };
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
2
|
export interface MyProfileBodyI {
|
|
3
|
-
account
|
|
4
|
-
details
|
|
5
|
-
email
|
|
3
|
+
account?: ReactNode;
|
|
4
|
+
details?: ReactNode;
|
|
5
|
+
email?: ReactNode;
|
|
6
6
|
isLoading: boolean;
|
|
7
|
-
payment
|
|
7
|
+
payment?: ReactNode;
|
|
8
|
+
rolePermissions?: ReactNode;
|
|
8
9
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ReducedRoleType } from '../../../helpers';
|
|
2
|
+
export type ActionsPermitted = 'view' | 'add' | 'edit' | 'delete';
|
|
3
|
+
export interface RolePermissionI {
|
|
4
|
+
id: string;
|
|
5
|
+
label: string;
|
|
6
|
+
permissions: Record<ActionsPermitted, string[]>;
|
|
7
|
+
}
|
|
8
|
+
export type RolePermissionObject = Record<ReducedRoleType, Record<ActionsPermitted, string[]>>;
|
|
9
|
+
export interface RolePermissionsTabI {
|
|
10
|
+
selected: RolePermissionObject;
|
|
11
|
+
onSelect(object: RolePermissionObject): void;
|
|
12
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export const featuresSelectedMock = {
|
|
2
|
+
member: {
|
|
3
|
+
view: [
|
|
4
|
+
'property-data',
|
|
5
|
+
'financial-data',
|
|
6
|
+
'partners',
|
|
7
|
+
'folders',
|
|
8
|
+
'home-items',
|
|
9
|
+
'rooms',
|
|
10
|
+
'rooms-items',
|
|
11
|
+
],
|
|
12
|
+
add: [],
|
|
13
|
+
edit: [],
|
|
14
|
+
delete: [],
|
|
15
|
+
},
|
|
16
|
+
contributor: {
|
|
17
|
+
view: [
|
|
18
|
+
'property-data',
|
|
19
|
+
'financial-data',
|
|
20
|
+
'partners',
|
|
21
|
+
'folders',
|
|
22
|
+
'home-items',
|
|
23
|
+
'rooms',
|
|
24
|
+
'rooms-items',
|
|
25
|
+
],
|
|
26
|
+
add: ['home-items', 'rooms', 'rooms-items'],
|
|
27
|
+
edit: ['folders', 'rooms', 'rooms-items'],
|
|
28
|
+
delete: ['rooms-items'],
|
|
29
|
+
},
|
|
30
|
+
manager: {
|
|
31
|
+
view: [
|
|
32
|
+
'property-data',
|
|
33
|
+
'financial-data',
|
|
34
|
+
'partners',
|
|
35
|
+
'folders',
|
|
36
|
+
'home-items',
|
|
37
|
+
'rooms',
|
|
38
|
+
'rooms-items',
|
|
39
|
+
],
|
|
40
|
+
add: [
|
|
41
|
+
'property-data',
|
|
42
|
+
'financial-data',
|
|
43
|
+
'partners',
|
|
44
|
+
'folders',
|
|
45
|
+
'home-items',
|
|
46
|
+
'rooms',
|
|
47
|
+
'rooms-items',
|
|
48
|
+
],
|
|
49
|
+
edit: [
|
|
50
|
+
'property-data',
|
|
51
|
+
'financial-data',
|
|
52
|
+
'partners',
|
|
53
|
+
'folders',
|
|
54
|
+
'home-items',
|
|
55
|
+
'rooms',
|
|
56
|
+
'rooms-items',
|
|
57
|
+
],
|
|
58
|
+
delete: [
|
|
59
|
+
'property-data',
|
|
60
|
+
'financial-data',
|
|
61
|
+
'partners',
|
|
62
|
+
'folders',
|
|
63
|
+
'home-items',
|
|
64
|
+
'rooms',
|
|
65
|
+
'rooms-items',
|
|
66
|
+
],
|
|
67
|
+
},
|
|
68
|
+
};
|