11import { FormattedMessage , useIntl } from "@edx/frontend-platform/i18n" ;
22import { Button , ButtonGroup , Form , Stack } from "@openedx/paragon" ;
33import { useConfigureSubsection , useCourseDetails , useCourseItemData } from "@src/course-outline/data/apiHooks" ;
4+ import { getProctoredExamsFlag , getTimedExamsFlag } from "@src/course-outline/data/selectors" ;
45import { ConfigureSubsectionData } from "@src/course-outline/data/types" ;
56import { useOutlineSidebarContext } from "@src/course-outline/outline-sidebar/OutlineSidebarContext" ;
67import { useCourseAuthoringContext } from "@src/CourseAuthoringContext" ;
78import { VisibilityTypes } from "@src/data/constants" ;
9+ import AdvancedTab from "@src/generic/configure-modal/AdvancedTab" ;
810import { DatepickerControl , DATEPICKER_TYPES } from "@src/generic/datepicker-control" ;
911import { SidebarContent , SidebarSection } from "@src/generic/sidebar"
1012import { useStateWithCallback } from "@src/hooks" ;
11- import { FormEvent , useState } from "react" ;
13+ import { useState } from "react" ;
14+ import { useSelector } from "react-redux" ;
1215import messages from './messages' ;
1316
1417interface Props {
@@ -102,7 +105,7 @@ const GradingSection = ({ subsectionId, onChange }: SubProps) => {
102105 </ Button >
103106 </ ButtonGroup >
104107 { graded &&
105- < Form . Group >
108+ < Form . Group className = "mt-2" >
106109 < Form . Label className = "x-small" >
107110 < FormattedMessage { ...messages . subsectionGradingDropdownLabel } />
108111 </ Form . Label >
@@ -120,7 +123,7 @@ const GradingSection = ({ subsectionId, onChange }: SubProps) => {
120123 </ Form . Group >
121124 }
122125 { ! courseDetails ?. selfPaced && graded &&
123- < Stack className = "mt-3" direction = "horizontal" gap = { 3 } >
126+ < Stack direction = "horizontal" gap = { 3 } >
124127 < DatepickerControl
125128 type = { DATEPICKER_TYPES . date }
126129 value = { localState ?. dueDate }
@@ -142,6 +145,144 @@ const GradingSection = ({ subsectionId, onChange }: SubProps) => {
142145 ) ;
143146}
144147
148+ const VisibilitySection = ( { subsectionId, onChange } : SubProps ) => {
149+ const intl = useIntl ( ) ;
150+ const { data : itemData } = useCourseItemData ( subsectionId ) ;
151+ const [ localState , setLocalState ] = useStateWithCallback < Partial < ConfigureSubsectionData > > (
152+ {
153+ isVisibleToStaffOnly : itemData ?. visibilityState === VisibilityTypes . STAFF_ONLY ,
154+ hideAfterDue : itemData ?. hideAfterDue ,
155+ } ,
156+ ( val ) => onChange ( val || { } )
157+ ) ;
158+
159+ return (
160+ < SidebarSection
161+ title = { intl . formatMessage ( messages . subsectionGradingTitle ) }
162+ >
163+ < ButtonGroup toggle >
164+ < Button
165+ variant = { localState ?. isVisibleToStaffOnly ? 'outline-primary' : 'primary' }
166+ onClick = { ( ) => setLocalState ( { ...localState , isVisibleToStaffOnly : false } ) }
167+ >
168+ < FormattedMessage { ...messages . subsectionVisibilityStudentVisible } />
169+ </ Button >
170+ < Button
171+ variant = { localState ?. isVisibleToStaffOnly ? 'primary' : 'outline-primary' }
172+ onClick = { ( ) => setLocalState ( {
173+ ...localState ,
174+ isVisibleToStaffOnly : true ,
175+ hideAfterDue : false ,
176+ } ) }
177+ >
178+ < FormattedMessage { ...messages . subsectionVisibilityStaffOnly } />
179+ </ Button >
180+ </ ButtonGroup >
181+ { ! localState ?. isVisibleToStaffOnly && < Form . Checkbox
182+ checked = { localState ?. hideAfterDue }
183+ className = "mt-2"
184+ onChange = { ( e ) => setLocalState ( {
185+ ...localState ,
186+ hideAfterDue : e . target . checked ,
187+ isVisibleToStaffOnly : false ,
188+ } ) }
189+ >
190+ < FormattedMessage { ...messages . subsectionVisibilityHideAfterDueLabel } />
191+ </ Form . Checkbox > }
192+ </ SidebarSection >
193+ ) ;
194+ }
195+
196+ const AssessmentResultVisibilitySection = ( { subsectionId, onChange } : SubProps ) => {
197+ const intl = useIntl ( ) ;
198+ const { data : itemData } = useCourseItemData ( subsectionId ) ;
199+ const [ localState , setLocalState ] = useStateWithCallback < Partial < ConfigureSubsectionData > > (
200+ {
201+ showCorrectness : itemData ?. showCorrectness ,
202+ } ,
203+ ( val ) => onChange ( val || { } )
204+ ) ;
205+
206+ return (
207+ < SidebarSection
208+ title = { intl . formatMessage ( messages . subsectionAssessmentResultsTitle ) }
209+ >
210+ < ButtonGroup toggle >
211+ < Button
212+ variant = { localState ?. showCorrectness === "always" ? 'primary' : 'outline-primary' }
213+ onClick = { ( ) => setLocalState ( { showCorrectness : "always" } ) }
214+ >
215+ < FormattedMessage { ...messages . subsectionAssessmentResultsShowBtn } />
216+ </ Button >
217+ < Button
218+ variant = { [ "never" , "past_due" ] . includes ( localState ?. showCorrectness || "" ) ? 'primary' : 'outline-primary' }
219+ onClick = { ( ) => {
220+ if ( localState ?. showCorrectness === "always" ) {
221+ setLocalState ( { showCorrectness : "never" } )
222+ }
223+ } }
224+ >
225+ < FormattedMessage { ...messages . subsectionAssessmentResultsHideBtn } />
226+ </ Button >
227+ </ ButtonGroup >
228+ < Form . Checkbox
229+ checked = { localState ?. showCorrectness === "past_due" }
230+ className = "mt-2"
231+ onChange = { ( ) => setLocalState ( { showCorrectness : "past_due" } ) }
232+ >
233+ < FormattedMessage { ...messages . subsectionAssessmentResultsCheckbox } />
234+ </ Form . Checkbox >
235+ </ SidebarSection >
236+ ) ;
237+ }
238+
239+ const SpecialExamSection = ( { subsectionId, onChange } : SubProps ) => {
240+ const intl = useIntl ( ) ;
241+ const { data : itemData } = useCourseItemData ( subsectionId ) ;
242+ const enableTimedExams = useSelector ( getTimedExamsFlag ) ;
243+ const enableProctoredExams = useSelector ( getProctoredExamsFlag ) ;
244+ const [ localState , setLocalState ] = useStateWithCallback < Partial < ConfigureSubsectionData > > (
245+ {
246+ showCorrectness : itemData ?. showCorrectness ,
247+ } ,
248+ ( val ) => onChange ( val || { } )
249+ ) ;
250+
251+ const setFieldValue = ( key : keyof ConfigureSubsectionData , value : any ) => {
252+ setLocalState ( {
253+ ...localState ,
254+ [ key ] : value ,
255+ } )
256+ }
257+
258+ return (
259+ < SidebarSection
260+ title = { intl . formatMessage ( messages . subsectionSpecialExamTitle ) }
261+ >
262+ < AdvancedTab
263+ values = { {
264+ isProctoredExam : itemData ?. isProctoredExam ,
265+ isTimeLimited : itemData ?. isTimeLimited ,
266+ isOnboardingExam : itemData ?. isOnboardingExam ,
267+ isPracticeExam : itemData ?. isPracticeExam ,
268+ defaultTimeLimitMinutes : itemData ?. defaultTimeLimitMinutes ,
269+ examReviewRules : itemData ?. examReviewRules ,
270+ } }
271+ setFieldValue = { setFieldValue }
272+ prereqs = { itemData ?. prereqs }
273+ releasedToStudents = { itemData ?. releasedToStudents }
274+ wasExamEverLinkedWithExternal = { itemData ?. wasExamEverLinkedWithExternal }
275+ enableProctoredExams = { enableProctoredExams }
276+ enableTimedExams = { enableTimedExams }
277+ supportsOnboarding = { itemData ?. supportsOnboarding }
278+ showReviewRules = { itemData ?. showReviewRules }
279+ wasProctoredExam = { itemData ?. isProctoredExam }
280+ onlineProctoringRules = { itemData ?. onlineProctoringRules }
281+ />
282+ </ SidebarSection >
283+ ) ;
284+ }
285+
145286export const SubsectionSettings = ( { subsectionId } : Props ) => {
146287 const { courseId } = useCourseAuthoringContext ( ) ;
147288 const { data : courseDetails } = useCourseDetails ( courseId ) ;
@@ -186,6 +327,18 @@ export const SubsectionSettings = ({ subsectionId }: Props) => {
186327 subsectionId = { subsectionId }
187328 onChange = { onChange }
188329 />
330+ < VisibilitySection
331+ subsectionId = { subsectionId }
332+ onChange = { onChange }
333+ />
334+ < AssessmentResultVisibilitySection
335+ subsectionId = { subsectionId }
336+ onChange = { onChange }
337+ />
338+ < SpecialExamSection
339+ subsectionId = { subsectionId }
340+ onChange = { onChange }
341+ />
189342 </ SidebarContent >
190343 )
191344}
0 commit comments