forked from openedx/frontend-app-authoring
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathUnitInfoSidebar.tsx
More file actions
239 lines (225 loc) · 7.66 KB
/
UnitInfoSidebar.tsx
File metadata and controls
239 lines (225 loc) · 7.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n';
import { useParams } from 'react-router-dom';
import { ComponentCountSnippet, getItemIcon } from '@src/generic/block-type-utils';
import { SidebarContent, SidebarSection, SidebarTitle } from '@src/generic/sidebar';
import { useEffect, useMemo } from 'react';
import { Tag } from '@openedx/paragon/icons';
import { ContentTagsSnippet } from '@src/content-tags-drawer';
import configureMessages from '@src/generic/configure-modal/messages';
import {
Button, ButtonGroup, Tab, Tabs,
} from '@openedx/paragon';
import { useDispatch, useSelector } from 'react-redux';
import { useIframe } from '@src/generic/hooks/context/hooks';
import { AccessEditComponent, DiscussionEditComponent } from '@src/generic/configure-modal/UnitTab';
import { Form, Formik } from 'formik';
import { getCourseUnitData, getCourseVerticalChildren } from '@src/course-unit/data/selectors';
import { messageTypes, PUBLISH_TYPES, UNIT_VISIBILITY_STATES } from '@src/course-unit/constants';
import { editCourseUnitVisibilityAndData } from '@src/course-unit/data/thunk';
import PublishControls from './PublishControls';
import { useUnitSidebarContext } from '../UnitSidebarContext';
import messages from './messages';
/**
* Component to show unit details: Publish status, Component counts and Content Tags.
*
* It's using in the details tab of the unit info sidebar.
*/
const UnitInfoDetails = () => {
const intl = useIntl();
const { blockId } = useParams();
const courseVerticalChildren = useSelector(getCourseVerticalChildren);
if (blockId === undefined) {
// istanbul ignore next - This shouldn't be possible; it's just here to satisfy the type checker.
throw new Error('Error: route is missing blockId.');
}
const componentData: Record<string, number> = useMemo(() => (
// @ts-ignore
courseVerticalChildren.children.reduce<Record<string, number>>(
(acc, { blockType }) => {
acc[blockType] = (acc[blockType] ?? 0) + 1;
return acc;
},
{},
)
), [courseVerticalChildren.children]);
return (
<SidebarContent>
<PublishControls blockId={blockId} hideCopyButton />
<SidebarSection
title={intl.formatMessage(messages.sidebarSectionSummary)}
icon={getItemIcon('unit')}
>
{componentData && <ComponentCountSnippet componentData={componentData} />}
</SidebarSection>
<SidebarSection
title={intl.formatMessage(messages.sidebarSectionTaxonomies)}
icon={Tag}
>
<ContentTagsSnippet contentId={blockId} />
</SidebarSection>
</SidebarContent>
);
};
/**
* Component with forms to edit unit settings.
*
* It's using in the settings tab of the unit info sidebar.
*/
const UnitInfoSettings = () => {
const dispatch = useDispatch();
const intl = useIntl();
const { sendMessageToIframe } = useIframe();
const {
id,
visibilityState,
discussionEnabled,
userPartitionInfo,
} = useSelector(getCourseUnitData);
const visibleToStaffOnly = visibilityState === UNIT_VISIBILITY_STATES.staffOnly;
const handleUpdate = async (
isVisible: boolean,
groupAccess: Object | null,
isDiscussionEnabled: boolean,
) => {
// oxlint-disable-next-line @typescript-eslint/await-thenable - this dispatch() IS returning a promise.
await dispatch(editCourseUnitVisibilityAndData(
id,
PUBLISH_TYPES.republish,
isVisible,
groupAccess,
isDiscussionEnabled,
() => sendMessageToIframe(messageTypes.completeManageXBlockAccess, { locator: id }),
id,
));
};
/* istanbul ignore next */
const handleSaveGroups = async (data, { resetForm }) => {
const groupAccess = {};
if (data.selectedPartitionIndex >= 0) {
const partitionId = userPartitionInfo.selectablePartitions[data.selectedPartitionIndex].id;
groupAccess[partitionId] = data.selectedGroups.map(g => parseInt(g, 10));
}
await handleUpdate(visibleToStaffOnly, groupAccess, discussionEnabled);
resetForm({ values: data });
};
/* istanbul ignore next */
const getSelectedGroups = () => {
if (userPartitionInfo?.selectedPartitionIndex >= 0) {
return userPartitionInfo?.selectablePartitions[userPartitionInfo?.selectedPartitionIndex]
?.groups
.filter(({ selected }) => selected)
// eslint-disable-next-line @typescript-eslint/no-shadow
.map(({ id }) => `${id}`)
|| [];
}
return [];
};
const initialValues = useMemo(() => (
{
selectedPartitionIndex: userPartitionInfo?.selectedPartitionIndex,
selectedGroups: getSelectedGroups(),
}
), [userPartitionInfo]);
return (
<SidebarContent>
<SidebarSection
title={intl.formatMessage(messages.sidebarInfoVisibilityTitle)}
>
<ButtonGroup toggle>
<Button
variant={visibleToStaffOnly ? 'outline-primary' : 'primary'}
onClick={() => handleUpdate(false, null, discussionEnabled)}
>
<FormattedMessage {...messages.sidebarInfoVisibilityStudentLabel} />
</Button>
<Button
variant={visibleToStaffOnly ? 'primary' : 'outline-primary'}
onClick={() => handleUpdate(true, null, discussionEnabled)}
>
<FormattedMessage {...messages.sidebarInfoVisibilityStaffLabel} />
</Button>
</ButtonGroup>
</SidebarSection>
<SidebarSection
title={intl.formatMessage(messages.sidebarInfoAccessTitle)}
>
<Formik
initialValues={initialValues}
onSubmit={handleSaveGroups}
>
{({
values, setFieldValue, dirty,
}) => (
<Form>
<AccessEditComponent
selectedPartitionIndex={values.selectedPartitionIndex}
setFieldValue={setFieldValue}
userPartitionInfo={userPartitionInfo}
selectedGroups={values.selectedGroups}
/>
{dirty && (
<Button className="mt-3" type="submit" variant="primary">
<FormattedMessage {...messages.visibilitySaveGroupsButton} />
</Button>
)}
</Form>
)}
</Formik>
</SidebarSection>
<SidebarSection
title={intl.formatMessage(configureMessages.discussionEnabledSectionTitle)}
>
<DiscussionEditComponent
discussionEnabled={discussionEnabled}
handleDiscussionChange={(e) => handleUpdate(visibleToStaffOnly, null, e.target.checked)}
/>
</SidebarSection>
</SidebarContent>
);
};
/**
* Main component that renders the tabs of the info sidebar.
*/
export const UnitInfoSidebar = () => {
const intl = useIntl();
const currentItemData = useSelector(getCourseUnitData);
const {
currentTabKey,
setCurrentTabKey,
} = useUnitSidebarContext();
useEffect(() => {
// Set default Tab key
setCurrentTabKey('details');
}, []);
return (
<div>
<SidebarTitle
title={currentItemData.displayName}
icon={getItemIcon('unit')}
/>
<Tabs
id="unit-info-sidebar-tabs"
className="my-2 d-flex justify-content-around"
activeKey={currentTabKey}
onSelect={setCurrentTabKey}
>
<Tab
eventKey="details"
title={intl.formatMessage(messages.sidebarInfoDetailsTab)}
>
<div className="mt-4">
<UnitInfoDetails />
</div>
</Tab>
<Tab
eventKey="settings"
title={intl.formatMessage(messages.sidebarInfoSettingsTab)}
>
<div className="mt-4">
<UnitInfoSettings />
</div>
</Tab>
</Tabs>
</div>
);
};