@griddo/ax 11.15.9 → 11.16.0-rc.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/package.json +2 -2
- package/src/__tests__/components/RestrictionsModal/CurrentAccessList/CurrentAccessList.test.tsx +173 -0
- package/src/__tests__/components/RestrictionsModal/OptionsStep/OptionsStep.test.tsx +160 -0
- package/src/__tests__/components/RestrictionsModal/RestrictionsModal.test.tsx +134 -0
- package/src/__tests__/components/RestrictionsModal/RolesList/RolesList.test.tsx +234 -0
- package/src/__tests__/components/RestrictionsModal/UsersList/UsersList.test.tsx +288 -0
- package/src/__tests__/hooks/users.test.tsx +382 -0
- package/src/api/pages.tsx +24 -1
- package/src/api/sites.tsx +14 -14
- package/src/api/structuredData.tsx +17 -7
- package/src/components/Browser/index.tsx +12 -0
- package/src/components/Browser/style.tsx +5 -1
- package/src/components/ConfigPanel/Form/index.tsx +11 -10
- package/src/components/ContentItem/index.tsx +31 -0
- package/src/components/ContentItem/style.tsx +43 -0
- package/src/components/Fields/ImageField/index.tsx +2 -1
- package/src/components/Fields/RadioField/index.tsx +3 -3
- package/src/components/Fields/RadioField/style.tsx +1 -1
- package/src/components/FloatingNote/index.tsx +12 -1
- package/src/components/FloatingNote/style.tsx +2 -1
- package/src/components/Modal/style.tsx +11 -0
- package/src/components/PageFinder/SelectionListItem/index.tsx +27 -17
- package/src/components/PageFinder/index.tsx +65 -28
- package/src/components/PageFinder/style.tsx +0 -1
- package/src/components/RestrictionsModal/CurrentAccessList/index.tsx +74 -0
- package/src/components/RestrictionsModal/CurrentAccessList/style.tsx +155 -0
- package/src/components/RestrictionsModal/OptionsStep/index.tsx +55 -0
- package/src/components/RestrictionsModal/OptionsStep/style.tsx +29 -0
- package/src/components/RestrictionsModal/RolesList/RoleItem/index.tsx +54 -0
- package/src/components/RestrictionsModal/RolesList/RoleItem/style.tsx +34 -0
- package/src/components/RestrictionsModal/RolesList/index.tsx +107 -0
- package/src/components/RestrictionsModal/RolesList/style.tsx +76 -0
- package/src/components/RestrictionsModal/UsersList/UserItem/index.tsx +78 -0
- package/src/components/RestrictionsModal/UsersList/UserItem/style.tsx +73 -0
- package/src/components/RestrictionsModal/UsersList/index.tsx +151 -0
- package/src/components/RestrictionsModal/UsersList/style.tsx +55 -0
- package/src/components/RestrictionsModal/index.tsx +181 -0
- package/src/components/RestrictionsModal/style.tsx +31 -0
- package/src/components/RestrictionsModal/utils.tsx +24 -0
- package/src/components/ScheduleModal/index.tsx +32 -8
- package/src/components/SelectionComponent/index.tsx +65 -0
- package/src/components/SelectionComponent/style.tsx +69 -0
- package/src/components/TableFilters/RoleFilter/index.tsx +3 -3
- package/src/components/TableList/TableItem/style.tsx +7 -13
- package/src/components/Tabs/index.tsx +2 -1
- package/src/components/UserRolesAndSites/index.tsx +2 -2
- package/src/components/index.tsx +6 -0
- package/src/containers/PageEditor/actions.tsx +43 -0
- package/src/containers/PageEditor/constants.tsx +1 -0
- package/src/containers/PageEditor/interfaces.tsx +26 -19
- package/src/containers/PageEditor/reducer.tsx +32 -19
- package/src/containers/Users/reducer.tsx +1 -0
- package/src/helpers/forms.tsx +3 -1
- package/src/hooks/index.tsx +2 -1
- package/src/hooks/users.tsx +154 -14
- package/src/modules/ActivityLog/LogFilters/UserFilter/index.tsx +11 -12
- package/src/modules/Content/PageItem/index.tsx +17 -10
- package/src/modules/Content/index.tsx +1 -3
- package/src/modules/GlobalEditor/Editor/index.tsx +6 -0
- package/src/modules/GlobalEditor/PageBrowser/index.tsx +6 -0
- package/src/modules/GlobalEditor/Preview/index.tsx +4 -2
- package/src/modules/GlobalEditor/index.tsx +72 -45
- package/src/modules/Navigation/Menus/List/Table/ConfigPanel/Form/index.tsx +3 -5
- package/src/modules/Navigation/Menus/List/Table/ConfigPanel/index.tsx +4 -4
- package/src/modules/Navigation/Menus/List/Table/SidePanel/Form/ConnectedField/index.tsx +2 -3
- package/src/modules/Navigation/Menus/List/Table/SidePanel/Form/index.tsx +3 -4
- package/src/modules/Navigation/Menus/List/Table/SidePanel/index.tsx +5 -6
- package/src/modules/PageEditor/Editor/index.tsx +6 -0
- package/src/modules/PageEditor/PageBrowser/index.tsx +6 -0
- package/src/modules/PageEditor/Preview/index.tsx +9 -2
- package/src/modules/PageEditor/atoms.tsx +110 -3
- package/src/modules/PageEditor/index.tsx +76 -95
- package/src/modules/Settings/Integrations/IntegrationForm/index.tsx +1 -3
- package/src/modules/Sites/SitesList/GridView/GridSiteItem/style.tsx +19 -2
- package/src/modules/Sites/SitesList/WelcomeModal/FinalStep/index.tsx +2 -2
- package/src/modules/Sites/SitesList/WelcomeModal/utils.ts +2 -2
- package/src/modules/StructuredData/Form/ConnectedField/index.tsx +2 -2
- package/src/modules/StructuredData/Form/index.tsx +120 -75
- package/src/modules/StructuredData/Form/style.tsx +7 -1
- package/src/modules/StructuredData/StructuredDataList/GlobalPageItem/index.tsx +15 -8
- package/src/modules/StructuredData/StructuredDataList/StructuredDataItem/index.tsx +15 -9
- package/src/modules/StructuredData/{StructuredDataList/StructuredDataItem/utils.tsx → utils.tsx} +16 -2
- package/src/modules/Users/Profile/atoms.tsx +62 -4
- package/src/modules/Users/Profile/index.tsx +83 -4
- package/src/modules/Users/Profile/style.tsx +60 -0
- package/src/modules/Users/UserCreate/SiteItem/RolesModal/RoleItem/index.tsx +3 -5
- package/src/modules/Users/UserCreate/SiteItem/index.tsx +2 -2
- package/src/modules/Users/UserCreate/atoms.tsx +69 -0
- package/src/modules/Users/UserCreate/index.tsx +149 -70
- package/src/modules/Users/UserCreate/style.tsx +24 -1
- package/src/modules/Users/UserForm/index.tsx +260 -146
- package/src/modules/Users/UserForm/style.tsx +13 -1
- package/src/modules/Users/UserList/UserItem/index.tsx +3 -3
- package/src/types/index.tsx +19 -6
- package/src/types/logs.tsx +1 -1
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from "react";
|
|
2
|
+
import { useSelector } from "react-redux";
|
|
3
|
+
|
|
4
|
+
import { Loading, Modal } from "@ax/components";
|
|
5
|
+
import type { IModal, IRootState, IUser, PageAccessGrantsSummary } from "@ax/types";
|
|
6
|
+
|
|
7
|
+
import CurrentAccessList from "./CurrentAccessList";
|
|
8
|
+
import OptionsStep from "./OptionsStep";
|
|
9
|
+
import RolesList from "./RolesList";
|
|
10
|
+
import UsersList from "./UsersList";
|
|
11
|
+
import { fetchUsers } from "./utils";
|
|
12
|
+
|
|
13
|
+
import * as S from "./style";
|
|
14
|
+
|
|
15
|
+
type Step = "current" | "options" | "list";
|
|
16
|
+
const STEPS: Step[] = ["current", "options", "list"];
|
|
17
|
+
|
|
18
|
+
const RestrictionsModal = (props: IRestrictionsModalProps): JSX.Element => {
|
|
19
|
+
const { isOpen, toggleModal, currentAccessGrants, onUpdateAccessGrants, isSaving } = props;
|
|
20
|
+
const currentSiteInfo = useSelector((state: IRootState) => state.sites.currentSiteInfo);
|
|
21
|
+
const [currentStep, setCurrentStep] = useState<Step>("options");
|
|
22
|
+
const [restrictionType, setRestrictionType] = useState<"users" | "roles">("users");
|
|
23
|
+
const [selectedUsers, setSelectedUsers] = useState<IUser[]>([]);
|
|
24
|
+
const [isLoadingUsers, setIsLoadingUsers] = useState(false);
|
|
25
|
+
const hasInitializedRef = useRef(false);
|
|
26
|
+
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
if (currentAccessGrants?.users && currentAccessGrants?.users.length > 0 && isOpen) {
|
|
29
|
+
const initializeSelected = async () => {
|
|
30
|
+
setIsLoadingUsers(true);
|
|
31
|
+
const data = await fetchUsers("", currentSiteInfo);
|
|
32
|
+
if (data) {
|
|
33
|
+
const selected = data.filter((user: IUser) => currentAccessGrants.users.includes(user.id!));
|
|
34
|
+
setSelectedUsers(selected);
|
|
35
|
+
}
|
|
36
|
+
setIsLoadingUsers(false);
|
|
37
|
+
};
|
|
38
|
+
initializeSelected();
|
|
39
|
+
}
|
|
40
|
+
}, [currentAccessGrants?.users, currentSiteInfo, isOpen]);
|
|
41
|
+
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
if (isOpen && !hasInitializedRef.current) {
|
|
44
|
+
hasInitializedRef.current = true;
|
|
45
|
+
|
|
46
|
+
// Determine initial step and restriction type
|
|
47
|
+
if (currentAccessGrants?.users?.length) {
|
|
48
|
+
setRestrictionType("users");
|
|
49
|
+
setCurrentStep("current");
|
|
50
|
+
} else if (currentAccessGrants?.roles?.length) {
|
|
51
|
+
setRestrictionType("roles");
|
|
52
|
+
setCurrentStep("current");
|
|
53
|
+
} else {
|
|
54
|
+
setRestrictionType("users");
|
|
55
|
+
setCurrentStep("options");
|
|
56
|
+
}
|
|
57
|
+
} else if (!isOpen) {
|
|
58
|
+
hasInitializedRef.current = false;
|
|
59
|
+
}
|
|
60
|
+
}, [isOpen, currentAccessGrants]);
|
|
61
|
+
|
|
62
|
+
const stepIndex = STEPS.indexOf(currentStep);
|
|
63
|
+
|
|
64
|
+
const handleNext = () => {
|
|
65
|
+
setCurrentStep(STEPS[stepIndex + 1]);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const handleEdit = () => {
|
|
69
|
+
setCurrentStep("list");
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const handleResetAccess = async () => {
|
|
73
|
+
setCurrentStep("options");
|
|
74
|
+
const newAccessGrants = {
|
|
75
|
+
users: [],
|
|
76
|
+
roles: [],
|
|
77
|
+
};
|
|
78
|
+
setSelectedUsers([]);
|
|
79
|
+
await onUpdateAccessGrants(newAccessGrants);
|
|
80
|
+
toggleModal();
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const handleClose = () => {
|
|
84
|
+
setCurrentStep("options");
|
|
85
|
+
setRestrictionType("users");
|
|
86
|
+
setSelectedUsers([]);
|
|
87
|
+
toggleModal();
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const handleApply = async (ids: number[]) => {
|
|
91
|
+
const newAccessGrants = {
|
|
92
|
+
users: restrictionType === "users" ? ids : [],
|
|
93
|
+
roles: restrictionType === "roles" ? ids : [],
|
|
94
|
+
};
|
|
95
|
+
setSelectedUsers([]);
|
|
96
|
+
await onUpdateAccessGrants(newAccessGrants);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const handleRestrictionTypeChange = (value: string) => {
|
|
100
|
+
if (value === "users" || value === "roles") {
|
|
101
|
+
setRestrictionType(value);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const getTitle = (): string => {
|
|
106
|
+
switch (currentStep) {
|
|
107
|
+
case "current":
|
|
108
|
+
case "list":
|
|
109
|
+
return "Page restrictions";
|
|
110
|
+
default:
|
|
111
|
+
return "Page restrictions";
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<Modal
|
|
117
|
+
isOpen={isOpen}
|
|
118
|
+
hide={handleClose}
|
|
119
|
+
title={getTitle()}
|
|
120
|
+
size="S"
|
|
121
|
+
height={currentStep === "options" || currentStep === "current" ? "auto" : 640}
|
|
122
|
+
>
|
|
123
|
+
<S.ModalContent>
|
|
124
|
+
{currentStep === "current" &&
|
|
125
|
+
(isLoadingUsers ? (
|
|
126
|
+
<S.LoadingContainer>
|
|
127
|
+
<Loading />
|
|
128
|
+
</S.LoadingContainer>
|
|
129
|
+
) : (
|
|
130
|
+
<CurrentAccessList
|
|
131
|
+
restrictionType={restrictionType}
|
|
132
|
+
currentAccessGrants={currentAccessGrants}
|
|
133
|
+
onEdit={handleEdit}
|
|
134
|
+
users={selectedUsers}
|
|
135
|
+
/>
|
|
136
|
+
))}
|
|
137
|
+
|
|
138
|
+
{currentStep === "options" && (
|
|
139
|
+
<OptionsStep
|
|
140
|
+
restrictionType={restrictionType}
|
|
141
|
+
onRestrictionTypeChange={handleRestrictionTypeChange}
|
|
142
|
+
onCancel={handleClose}
|
|
143
|
+
onNext={handleNext}
|
|
144
|
+
/>
|
|
145
|
+
)}
|
|
146
|
+
|
|
147
|
+
{currentStep === "list" && (
|
|
148
|
+
<>
|
|
149
|
+
{restrictionType === "users" && (
|
|
150
|
+
<UsersList
|
|
151
|
+
onCancel={handleClose}
|
|
152
|
+
onApply={handleApply}
|
|
153
|
+
selectedUserIds={currentAccessGrants?.users || []}
|
|
154
|
+
isSaving={isSaving}
|
|
155
|
+
onResetAccess={handleResetAccess}
|
|
156
|
+
users={selectedUsers}
|
|
157
|
+
/>
|
|
158
|
+
)}
|
|
159
|
+
{restrictionType === "roles" && (
|
|
160
|
+
<RolesList
|
|
161
|
+
onCancel={handleClose}
|
|
162
|
+
onApply={handleApply}
|
|
163
|
+
selectedRoleIds={currentAccessGrants?.roles || []}
|
|
164
|
+
isSaving={isSaving}
|
|
165
|
+
onResetAccess={handleResetAccess}
|
|
166
|
+
/>
|
|
167
|
+
)}
|
|
168
|
+
</>
|
|
169
|
+
)}
|
|
170
|
+
</S.ModalContent>
|
|
171
|
+
</Modal>
|
|
172
|
+
);
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
interface IRestrictionsModalProps extends IModal {
|
|
176
|
+
currentAccessGrants?: PageAccessGrantsSummary;
|
|
177
|
+
onUpdateAccessGrants: (accessGrants: PageAccessGrantsSummary) => Promise<void>;
|
|
178
|
+
isSaving: boolean;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export default RestrictionsModal;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import styled from "styled-components";
|
|
2
|
+
|
|
3
|
+
const ModalContent = styled.div`
|
|
4
|
+
display: flex;
|
|
5
|
+
flex-direction: column;
|
|
6
|
+
height: 100%;
|
|
7
|
+
`;
|
|
8
|
+
|
|
9
|
+
const Text = styled.div`
|
|
10
|
+
${(p) => p.theme.textStyle.uiM};
|
|
11
|
+
margin-bottom: ${(p) => p.theme.spacing.m};
|
|
12
|
+
`;
|
|
13
|
+
|
|
14
|
+
const OptionsGroup = styled.div`
|
|
15
|
+
display: flex;
|
|
16
|
+
flex-direction: column;
|
|
17
|
+
gap: ${(p) => p.theme.spacing.xs};
|
|
18
|
+
margin: ${(p) => `${p.theme.spacing.m} 0`};
|
|
19
|
+
`;
|
|
20
|
+
|
|
21
|
+
const ListContainer = styled.div`
|
|
22
|
+
margin-top: ${(p) => p.theme.spacing.m};
|
|
23
|
+
`;
|
|
24
|
+
|
|
25
|
+
const OptionsList = styled.ul``;
|
|
26
|
+
|
|
27
|
+
const LoadingContainer = styled.div`
|
|
28
|
+
padding: ${(p) => p.theme.spacing.m};
|
|
29
|
+
`;
|
|
30
|
+
|
|
31
|
+
export { ListContainer, LoadingContainer, ModalContent, OptionsList, OptionsGroup, Text };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { users } from "@ax/api";
|
|
2
|
+
import { isReqOk } from "@ax/helpers";
|
|
3
|
+
import type { IUser } from "@ax/types";
|
|
4
|
+
|
|
5
|
+
const fetchUsers = async (query = "", currentSiteInfo?: { id: number } | null) => {
|
|
6
|
+
try {
|
|
7
|
+
const searchQuery = query.length ? `&query=${query}` : "";
|
|
8
|
+
const params = { filterQuery: "?order=name-asc", query: searchQuery };
|
|
9
|
+
const items = currentSiteInfo ? await users.getSiteUsers(params, currentSiteInfo.id) : await users.getUsers(params);
|
|
10
|
+
if (items && isReqOk(items.status)) {
|
|
11
|
+
let data = items.data;
|
|
12
|
+
// Filter to global users only when no specific site is selected
|
|
13
|
+
if (!currentSiteInfo) {
|
|
14
|
+
data = data.filter((user: IUser) => user.roles.some((role) => role.siteId === "global"));
|
|
15
|
+
}
|
|
16
|
+
return data;
|
|
17
|
+
}
|
|
18
|
+
} catch (e) {
|
|
19
|
+
console.log(e);
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export { fetchUsers };
|
|
@@ -7,7 +7,7 @@ import type { IModal } from "@ax/types";
|
|
|
7
7
|
import * as S from "./style";
|
|
8
8
|
|
|
9
9
|
const ScheduleModal = (props: IScheduleModal): JSX.Element => {
|
|
10
|
-
const { isOpen, toggleModal,
|
|
10
|
+
const { isOpen, toggleModal, scheduleDate, setScheduleDate, onSchedule } = props;
|
|
11
11
|
|
|
12
12
|
const [error, setError] = useState(false);
|
|
13
13
|
|
|
@@ -33,15 +33,24 @@ const ScheduleModal = (props: IScheduleModal): JSX.Element => {
|
|
|
33
33
|
}
|
|
34
34
|
};
|
|
35
35
|
|
|
36
|
-
const
|
|
36
|
+
const mainAction = {
|
|
37
|
+
title: "Schedule",
|
|
38
|
+
onClick: onSchedule,
|
|
39
|
+
disabled: error,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const secondaryAction = {
|
|
43
|
+
title: "Cancel",
|
|
44
|
+
onClick: () => toggleModal(),
|
|
45
|
+
};
|
|
37
46
|
|
|
38
47
|
return (
|
|
39
48
|
<Modal
|
|
40
49
|
isOpen={isOpen}
|
|
41
50
|
hide={toggleModal}
|
|
42
51
|
title="Schedule publication"
|
|
43
|
-
secondaryAction={
|
|
44
|
-
mainAction={
|
|
52
|
+
secondaryAction={secondaryAction}
|
|
53
|
+
mainAction={mainAction}
|
|
45
54
|
size="M"
|
|
46
55
|
height={330}
|
|
47
56
|
overflow="visible"
|
|
@@ -77,16 +86,26 @@ const ScheduleModal = (props: IScheduleModal): JSX.Element => {
|
|
|
77
86
|
);
|
|
78
87
|
};
|
|
79
88
|
|
|
80
|
-
const CancelScheduleModal = (props:
|
|
81
|
-
const { isOpen, toggleModal,
|
|
89
|
+
const CancelScheduleModal = (props: ICancelScheduleModal): JSX.Element => {
|
|
90
|
+
const { isOpen, toggleModal, onCancelSchedule } = props;
|
|
91
|
+
|
|
92
|
+
const mainAction = {
|
|
93
|
+
title: "Cancel Schedule",
|
|
94
|
+
onClick: onCancelSchedule,
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const secondaryAction = {
|
|
98
|
+
title: "Back",
|
|
99
|
+
onClick: () => toggleModal(),
|
|
100
|
+
};
|
|
82
101
|
|
|
83
102
|
return (
|
|
84
103
|
<Modal
|
|
85
104
|
isOpen={isOpen}
|
|
86
105
|
hide={toggleModal}
|
|
87
106
|
title="Cancel schedule publication"
|
|
88
|
-
secondaryAction={
|
|
89
|
-
mainAction={
|
|
107
|
+
secondaryAction={secondaryAction}
|
|
108
|
+
mainAction={mainAction}
|
|
90
109
|
size="S"
|
|
91
110
|
height={240}
|
|
92
111
|
>
|
|
@@ -103,6 +122,11 @@ const CancelScheduleModal = (props: IModal): JSX.Element => {
|
|
|
103
122
|
interface IScheduleModal extends IModal {
|
|
104
123
|
scheduleDate: { date: string; time: string };
|
|
105
124
|
setScheduleDate: (scheduleDate: { date: string; time: string }) => void;
|
|
125
|
+
onSchedule: () => void;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
interface ICancelScheduleModal extends IModal {
|
|
129
|
+
onCancelSchedule: () => void;
|
|
106
130
|
}
|
|
107
131
|
|
|
108
132
|
export { ScheduleModal, CancelScheduleModal };
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Button, CheckField, RadioField, TruncatedTooltip } from "@ax/components";
|
|
2
|
+
|
|
3
|
+
import * as S from "./style";
|
|
4
|
+
|
|
5
|
+
const SelectionComponent = (props: IProps): JSX.Element => {
|
|
6
|
+
const { name, value, label, description, isSelected, multi, btnText, btnAction, onToggle } = props;
|
|
7
|
+
|
|
8
|
+
const handleToggle = () => {
|
|
9
|
+
onToggle(value);
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const handleFieldClick = (e: React.MouseEvent) => {
|
|
13
|
+
e.stopPropagation();
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const handleKeyDown = (e: React.KeyboardEvent) => {
|
|
17
|
+
if (e.key === "Enter") {
|
|
18
|
+
e.preventDefault();
|
|
19
|
+
handleToggle();
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<S.Wrapper isSelected={isSelected} onClick={handleToggle} onKeyDown={handleKeyDown} role="button" tabIndex={0}>
|
|
25
|
+
{multi ? (
|
|
26
|
+
<button type="button" onClick={handleFieldClick} onKeyDown={handleKeyDown} tabIndex={-1}>
|
|
27
|
+
<CheckField name={name} value={value} checked={isSelected} onChange={() => onToggle(value)} tabIndex={-1} />
|
|
28
|
+
</button>
|
|
29
|
+
) : (
|
|
30
|
+
<button type="button" onClick={handleFieldClick} onKeyDown={handleKeyDown} tabIndex={-1}>
|
|
31
|
+
<RadioField name={name} value={value} checked={isSelected} onChange={() => onToggle(value)} tabIndex={-1} />
|
|
32
|
+
</button>
|
|
33
|
+
)}
|
|
34
|
+
<S.Info>
|
|
35
|
+
<S.LabelWrapper>
|
|
36
|
+
<TruncatedTooltip content={label} expanded top={-5}>
|
|
37
|
+
<S.Label>{label}</S.Label>
|
|
38
|
+
</TruncatedTooltip>
|
|
39
|
+
</S.LabelWrapper>
|
|
40
|
+
{description && <S.Description>{description}</S.Description>}
|
|
41
|
+
{btnText && (
|
|
42
|
+
<S.ButtonWrapper>
|
|
43
|
+
<Button type="button" buttonStyle="text" onClick={btnAction}>
|
|
44
|
+
{btnText}
|
|
45
|
+
</Button>
|
|
46
|
+
</S.ButtonWrapper>
|
|
47
|
+
)}
|
|
48
|
+
</S.Info>
|
|
49
|
+
</S.Wrapper>
|
|
50
|
+
);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
interface IProps {
|
|
54
|
+
name: string;
|
|
55
|
+
value: string;
|
|
56
|
+
label: string;
|
|
57
|
+
description?: string;
|
|
58
|
+
isSelected: boolean;
|
|
59
|
+
multi: boolean;
|
|
60
|
+
onToggle: (value: string) => void;
|
|
61
|
+
btnText?: string;
|
|
62
|
+
btnAction?: () => void;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export default SelectionComponent;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import styled from "styled-components";
|
|
2
|
+
|
|
3
|
+
const Wrapper = styled.div<{ isSelected: boolean }>`
|
|
4
|
+
position: relative;
|
|
5
|
+
display: flex;
|
|
6
|
+
border: ${(p) => (p.isSelected ? `1px solid ${p.theme.color.interactive01}` : `1px solid ${p.theme.color.uiLine}`)};
|
|
7
|
+
border-radius: ${(p) => p.theme.radii.s};
|
|
8
|
+
padding: ${(p) => p.theme.spacing.s};
|
|
9
|
+
background-color: ${(p) => p.theme.color.uiBackground02};
|
|
10
|
+
cursor: pointer;
|
|
11
|
+
:before {
|
|
12
|
+
content: "";
|
|
13
|
+
border-radius: ${(p) => p.theme.radii.s};
|
|
14
|
+
position: absolute;
|
|
15
|
+
top: 0;
|
|
16
|
+
left: 0;
|
|
17
|
+
width: 100%;
|
|
18
|
+
height: 100%;
|
|
19
|
+
opacity: 0;
|
|
20
|
+
transition: opacity 0.1s;
|
|
21
|
+
}
|
|
22
|
+
:hover:before {
|
|
23
|
+
background-color: ${(p) => p.theme.color.overlayHoverPrimary};
|
|
24
|
+
opacity: 1;
|
|
25
|
+
}
|
|
26
|
+
`;
|
|
27
|
+
|
|
28
|
+
const Info = styled.div`
|
|
29
|
+
display: flex;
|
|
30
|
+
flex-flow: column nowrap;
|
|
31
|
+
flex-grow: 1;
|
|
32
|
+
min-width: 0;
|
|
33
|
+
padding-left: ${(p) => p.theme.spacing.xs};
|
|
34
|
+
`;
|
|
35
|
+
|
|
36
|
+
const LabelWrapper = styled.div`
|
|
37
|
+
width: 100%;
|
|
38
|
+
min-width: 0;
|
|
39
|
+
> * {
|
|
40
|
+
min-width: 0;
|
|
41
|
+
max-width: 100%;
|
|
42
|
+
}
|
|
43
|
+
`;
|
|
44
|
+
|
|
45
|
+
const Label = styled.div`
|
|
46
|
+
width: 100%;
|
|
47
|
+
${(p) => p.theme.textStyle.uiL};
|
|
48
|
+
font-weight: 600;
|
|
49
|
+
color: ${(p) => p.theme.colors.textHighEmphasis};
|
|
50
|
+
overflow: hidden;
|
|
51
|
+
text-overflow: ellipsis;
|
|
52
|
+
white-space: nowrap;
|
|
53
|
+
display: block;
|
|
54
|
+
min-width: 0;
|
|
55
|
+
`;
|
|
56
|
+
|
|
57
|
+
const Description = styled.div`
|
|
58
|
+
${(p) => p.theme.textStyle.uiXS};
|
|
59
|
+
color: ${(p) => p.theme.colors.textMediumEmphasis};
|
|
60
|
+
margin-top: ${(p) => p.theme.spacing.xxs};
|
|
61
|
+
width: 100%;
|
|
62
|
+
min-width: 0;
|
|
63
|
+
`;
|
|
64
|
+
|
|
65
|
+
const ButtonWrapper = styled.div`
|
|
66
|
+
margin-top: ${(p) => p.theme.spacing.xs};
|
|
67
|
+
`;
|
|
68
|
+
|
|
69
|
+
export { Wrapper, Info, LabelWrapper, Description, Label, ButtonWrapper };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useLayoutEffect, useState } from "react";
|
|
2
2
|
|
|
3
3
|
import { CheckGroup, FloatingMenu, Icon, ListTitle } from "@ax/components";
|
|
4
4
|
import { areEquals } from "@ax/helpers";
|
|
5
|
-
import { IFilterValue, IQueryValue } from "@ax/types";
|
|
5
|
+
import type { IFilterValue, IQueryValue } from "@ax/types";
|
|
6
6
|
|
|
7
7
|
import * as S from "./style";
|
|
8
8
|
|
|
@@ -31,7 +31,7 @@ const RoleFilter = (props: IRoleFilterProps): JSX.Element => {
|
|
|
31
31
|
}
|
|
32
32
|
setSelectedValue(selection);
|
|
33
33
|
const queryFilters: IQueryValue[] = selection.map((value: string | number) => {
|
|
34
|
-
const label = filters
|
|
34
|
+
const label = filters?.find((opt: IFilterValue) => opt.value === value);
|
|
35
35
|
return { value, label: label?.title || "" };
|
|
36
36
|
});
|
|
37
37
|
filterItems(pointer, queryFilters);
|
|
@@ -23,17 +23,13 @@ export const Row = styled.div<{ selected: boolean; global?: boolean }>`
|
|
|
23
23
|
|
|
24
24
|
display: flex;
|
|
25
25
|
flex-flow: row;
|
|
26
|
-
border: 1px solid ${(p) => p.theme.color.uiLine};
|
|
26
|
+
border: 1px solid ${(p) => (p.selected ? `${p.theme.color.interactive01}` : p.theme.color.uiLine)};
|
|
27
27
|
border-radius: ${(p) => p.theme.radii.s};
|
|
28
28
|
margin-bottom: ${(p) => p.theme.spacing.xs};
|
|
29
29
|
cursor: pointer;
|
|
30
|
-
|
|
30
|
+
&:before {
|
|
31
31
|
content: "";
|
|
32
|
-
|
|
33
|
-
/* This border-radius does not comply with the design system, but it prevents
|
|
34
|
-
* a "gap" from appearing between the row and the selected row due to the
|
|
35
|
-
* border-radius not matching (because of the border-width) */
|
|
36
|
-
border-radius: 3px;
|
|
32
|
+
border-radius: 4px;
|
|
37
33
|
position: absolute;
|
|
38
34
|
top: 0;
|
|
39
35
|
left: 0;
|
|
@@ -43,10 +39,8 @@ export const Row = styled.div<{ selected: boolean; global?: boolean }>`
|
|
|
43
39
|
transition: opacity 0.1s;
|
|
44
40
|
pointer-events: none;
|
|
45
41
|
}
|
|
46
|
-
:
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
opacity: 1;
|
|
51
|
-
}
|
|
42
|
+
&:hover:before {
|
|
43
|
+
background-color: ${(p) => p.theme.color.overlayHoverPrimary};
|
|
44
|
+
opacity: 1;
|
|
45
|
+
}
|
|
52
46
|
`;
|
|
@@ -6,9 +6,10 @@ import * as S from "./style";
|
|
|
6
6
|
const Tabs = (props: ITabsProps): JSX.Element => {
|
|
7
7
|
const { tabs, icons, active, setSelectedTab, isInAppBar, noMargins, inversed, disabled } = props;
|
|
8
8
|
|
|
9
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: setSelectedTab is stable and should not trigger re-renders
|
|
9
10
|
useEffect(() => {
|
|
10
11
|
tabs && tabs.length === 1 && setSelectedTab(tabs[0]);
|
|
11
|
-
}, [tabs
|
|
12
|
+
}, [tabs]);
|
|
12
13
|
|
|
13
14
|
if (tabs) {
|
|
14
15
|
return (
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ElementsTooltip } from "@ax/components";
|
|
2
|
-
import type { IRole, ISite,
|
|
2
|
+
import type { IRole, ISite, UserRoleAssignment } from "@ax/types";
|
|
3
3
|
|
|
4
4
|
import RoleItem from "./RoleItem";
|
|
5
5
|
|
|
@@ -93,7 +93,7 @@ const UserRolesAndSites = (props: IUserRolesAndSitesProps): JSX.Element => {
|
|
|
93
93
|
|
|
94
94
|
interface IUserRolesAndSitesProps {
|
|
95
95
|
isSuperAdmin: boolean;
|
|
96
|
-
userRoles:
|
|
96
|
+
userRoles: UserRoleAssignment[];
|
|
97
97
|
sites: ISite[];
|
|
98
98
|
roles: IRole[];
|
|
99
99
|
showRows?: boolean;
|
package/src/components/index.tsx
CHANGED
|
@@ -8,6 +8,7 @@ import BulkSelectionOptions from "./BulkSelectionOptions";
|
|
|
8
8
|
import Button from "./Button";
|
|
9
9
|
import CategoryCell from "./CategoryCell";
|
|
10
10
|
import Circle from "./Circle";
|
|
11
|
+
import ContentItem from "./ContentItem";
|
|
11
12
|
import ConfigPanel from "./ConfigPanel";
|
|
12
13
|
import DragAndDrop from "./DragAndDrop";
|
|
13
14
|
import ElementsTooltip from "./ElementsTooltip";
|
|
@@ -95,9 +96,11 @@ import ProgressBar from "./ProgressBar";
|
|
|
95
96
|
import ReorderArrows from "./ReorderArrows";
|
|
96
97
|
import ResizePanel from "./ResizePanel";
|
|
97
98
|
import RestoreModal from "./RestoreModal";
|
|
99
|
+
import RestrictionsModal from "./RestrictionsModal";
|
|
98
100
|
import { CancelScheduleModal, ScheduleModal } from "./ScheduleModal";
|
|
99
101
|
import SearchField from "./SearchField";
|
|
100
102
|
import SearchTagsBar from "./SearchTagsBar";
|
|
103
|
+
import SelectionComponent from "./SelectionComponent";
|
|
101
104
|
import SharePageModal from "./SharePageModal";
|
|
102
105
|
import SideModal from "./SideModal";
|
|
103
106
|
import StatusTile from "./StatusTile";
|
|
@@ -150,6 +153,7 @@ export {
|
|
|
150
153
|
Circle,
|
|
151
154
|
ComponentArray,
|
|
152
155
|
ComponentContainer,
|
|
156
|
+
ContentItem,
|
|
153
157
|
ConditionalField,
|
|
154
158
|
ConfigPanel,
|
|
155
159
|
CustomizeFilters,
|
|
@@ -220,12 +224,14 @@ export {
|
|
|
220
224
|
ReorderArrows,
|
|
221
225
|
ResizePanel,
|
|
222
226
|
RestoreModal,
|
|
227
|
+
RestrictionsModal,
|
|
223
228
|
RichText,
|
|
224
229
|
RoleFilter,
|
|
225
230
|
ScheduleModal,
|
|
226
231
|
SearchField,
|
|
227
232
|
SearchTagsBar,
|
|
228
233
|
Select,
|
|
234
|
+
SelectionComponent,
|
|
229
235
|
SEOPreview,
|
|
230
236
|
SharePageModal,
|
|
231
237
|
SideModal,
|
|
@@ -51,6 +51,7 @@ import type {
|
|
|
51
51
|
ISchema,
|
|
52
52
|
IStructuredDataContent,
|
|
53
53
|
IUserEditing,
|
|
54
|
+
PageAccessGrantsSummary,
|
|
54
55
|
} from "@ax/types";
|
|
55
56
|
|
|
56
57
|
import type { Dispatch } from "redux";
|
|
@@ -69,6 +70,7 @@ import {
|
|
|
69
70
|
SET_IS_NEW_TRANSLATION,
|
|
70
71
|
SET_LAST_ELEMENT_ADDED_ID,
|
|
71
72
|
SET_LAST_TIMEOUT,
|
|
73
|
+
SET_PAGE_ACCESS_GRANTS,
|
|
72
74
|
SET_SCHEMA,
|
|
73
75
|
SET_SCROLL_EDITOR_ID,
|
|
74
76
|
SET_SELECTED_CONTENT,
|
|
@@ -96,6 +98,7 @@ import type {
|
|
|
96
98
|
ISetIsNewTranslation,
|
|
97
99
|
ISetLastElementAddedId,
|
|
98
100
|
ISetLastTimeout,
|
|
101
|
+
ISetPageAccessGrants,
|
|
99
102
|
ISetSchema,
|
|
100
103
|
ISetScrollEditorID,
|
|
101
104
|
ISetSelectedContent,
|
|
@@ -230,6 +233,10 @@ function setScrollEditorID(scrollEditorID: number | null): ISetScrollEditorID {
|
|
|
230
233
|
return { type: SET_SCROLL_EDITOR_ID, payload: { scrollEditorID } };
|
|
231
234
|
}
|
|
232
235
|
|
|
236
|
+
function setPageAccessGrants(accessGrants: PageAccessGrantsSummary): ISetPageAccessGrants {
|
|
237
|
+
return { type: SET_PAGE_ACCESS_GRANTS, payload: { accessGrants } };
|
|
238
|
+
}
|
|
239
|
+
|
|
233
240
|
function setTranslatedParent(): (dispatch: Dispatch, getState: any) => void {
|
|
234
241
|
return async (dispatch, getState) => {
|
|
235
242
|
try {
|
|
@@ -384,6 +391,7 @@ function getPage(
|
|
|
384
391
|
dispatch(setTemplateConfig(page.templateConfig));
|
|
385
392
|
dispatch(setUserEditing(page.editing));
|
|
386
393
|
dispatch(setCurrentPageStatus(pageLiveStatus));
|
|
394
|
+
dispatch(setPageAccessGrants(page.accessGrants));
|
|
387
395
|
}
|
|
388
396
|
} else {
|
|
389
397
|
if (!isNewTranslation) {
|
|
@@ -1632,6 +1640,40 @@ function schedulePublication(
|
|
|
1632
1640
|
};
|
|
1633
1641
|
}
|
|
1634
1642
|
|
|
1643
|
+
function updatePageAccessGrants(data: {
|
|
1644
|
+
siteID: number | "global";
|
|
1645
|
+
pageID: number | null;
|
|
1646
|
+
accessGrants: PageAccessGrantsSummary;
|
|
1647
|
+
}): (dispatch: Dispatch, getState: () => IRootState) => Promise<boolean> {
|
|
1648
|
+
return async (dispatch, getState) => {
|
|
1649
|
+
try {
|
|
1650
|
+
if (!data.pageID) {
|
|
1651
|
+
dispatch(setPageAccessGrants(data.accessGrants));
|
|
1652
|
+
updateEditorContent(0, "accessGrants", data.accessGrants)(dispatch, getState);
|
|
1653
|
+
return true;
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
const responseActions = {
|
|
1657
|
+
handleSuccess: () => {
|
|
1658
|
+
dispatch(setPageAccessGrants(data.accessGrants));
|
|
1659
|
+
},
|
|
1660
|
+
handleError: (response: any) => appActions.handleError(response)(dispatch),
|
|
1661
|
+
};
|
|
1662
|
+
|
|
1663
|
+
const callback = async () => {
|
|
1664
|
+
if (data.pageID) {
|
|
1665
|
+
await pages.setPageAccessGrants({ ...data, pageID: data.pageID });
|
|
1666
|
+
}
|
|
1667
|
+
};
|
|
1668
|
+
|
|
1669
|
+
return await handleRequest(callback, responseActions, [appActions.setIsSaving])(dispatch);
|
|
1670
|
+
} catch (e) {
|
|
1671
|
+
console.log(e);
|
|
1672
|
+
return false;
|
|
1673
|
+
}
|
|
1674
|
+
};
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1635
1677
|
export {
|
|
1636
1678
|
setEditorContent,
|
|
1637
1679
|
setTemplate,
|
|
@@ -1687,4 +1729,5 @@ export {
|
|
|
1687
1729
|
setScrollEditorID,
|
|
1688
1730
|
addComponentInArray,
|
|
1689
1731
|
replaceModuleInArray,
|
|
1732
|
+
updatePageAccessGrants,
|
|
1690
1733
|
};
|
|
@@ -24,6 +24,7 @@ export const SET_IS_IA_TRANSLATED = `${NAME}/SET_IS_IA_TRANSLATED`;
|
|
|
24
24
|
export const SET_LAST_TIMEOUT = `${NAME}/SET_LAST_TIMEOUT`;
|
|
25
25
|
export const RESET_PAGE_EDITOR_STORE = `${NAME}/RESET_PAGE_EDITOR_STORE`;
|
|
26
26
|
export const SET_SCROLL_EDITOR_ID = `${NAME}/SET_SCROLL_EDITOR_ID`;
|
|
27
|
+
export const SET_PAGE_ACCESS_GRANTS = `${NAME}/SET_PAGE_ACCESS_GRANTS`;
|
|
27
28
|
|
|
28
29
|
export const INITIAL_TEMPLATE = "BasicTemplate";
|
|
29
30
|
export const MULTIMEDIA_COMPONENTS = ["LinkableImage", "Video"];
|