@@ -5,13 +5,15 @@ import {
5
5
import { useDispatch , useSelector } from 'react-redux' ;
6
6
import { useIntl } from '@edx/frontend-platform/i18n' ;
7
7
import {
8
- Badge , Container , Layout , Button , Card , Spinner ,
8
+ Badge , Container , Layout , Card , Spinner , StatefulButton ,
9
9
} from '@openedx/paragon' ;
10
10
import { Helmet } from 'react-helmet' ;
11
11
12
12
import CourseStepper from '../generic/course-stepper' ;
13
13
import ConnectionErrorAlert from '../generic/ConnectionErrorAlert' ;
14
+ import AlertMessage from '../generic/alert-message' ;
14
15
import { RequestFailureStatuses } from '../data/constants' ;
16
+ import { STATEFUL_BUTTON_STATES } from '../constants' ;
15
17
import messages from './messages' ;
16
18
import {
17
19
getCurrentStage , getError , getLinkCheckInProgress , getLoadingStatus , getSavingStatus , getLinkCheckResult ,
@@ -53,14 +55,19 @@ const CourseOptimizerPage: FC<{ courseId: string }> = ({ courseId }) => {
53
55
const lastScannedAt = useSelector ( getLastScannedAt ) ;
54
56
const { msg : errorMessage } = useSelector ( getError ) ;
55
57
const isLoadingDenied = ( RequestFailureStatuses as string [ ] ) . includes ( loadingStatus ) ;
56
- const isSavingDenied = ( RequestFailureStatuses as string [ ] ) . includes ( savingStatus ) ;
57
58
const interval = useRef < number | undefined > ( undefined ) ;
58
59
const courseDetails = useModel ( 'courseDetails' , courseId ) ;
59
60
const linkCheckPresent = currentStage != null ? currentStage >= 0 : ! ! currentStage ;
60
61
const [ showStepper , setShowStepper ] = useState ( false ) ;
61
-
62
+ const [ scanResultsError , setScanResultsError ] = useState < string | null > ( null ) ;
63
+ const isSavingDenied = ( RequestFailureStatuses as string [ ] ) . includes ( savingStatus ) && ! errorMessage ;
62
64
const intl = useIntl ( ) ;
63
-
65
+ const getScanButtonState = ( ) => {
66
+ if ( linkCheckInProgress && ! errorMessage ) {
67
+ return STATEFUL_BUTTON_STATES . pending ;
68
+ }
69
+ return STATEFUL_BUTTON_STATES . default ;
70
+ } ;
64
71
const courseStepperSteps = [
65
72
{
66
73
title : intl . formatMessage ( messages . preparingStepTitle ) ,
@@ -131,10 +138,25 @@ const CourseOptimizerPage: FC<{ courseId: string }> = ({ courseId }) => {
131
138
} ) }
132
139
</ title >
133
140
</ Helmet >
141
+ { scanResultsError && (
142
+ < AlertMessage
143
+ variant = "danger"
144
+ title = ""
145
+ description = { scanResultsError }
146
+ dismissible
147
+ show = { ! ! scanResultsError }
148
+ onClose = { ( ) => setScanResultsError ( null ) }
149
+ className = "mt-3"
150
+ />
151
+ ) }
134
152
< Container size = "xl" className = "mt-4 px-4 export" >
135
153
< section className = "setting-items mb-4" >
136
154
< Layout
137
- lg = { [ { span : 12 } , { span : 0 } ] }
155
+ lg = { [ { span : 9 } , { span : 3 } ] }
156
+ md = { [ { span : 9 } , { span : 3 } ] }
157
+ sm = { [ { span : 9 } , { span : 3 } ] }
158
+ xs = { [ { span : 9 } , { span : 3 } ] }
159
+ xl = { [ { span : 9 } , { span : 3 } ] }
138
160
>
139
161
< Layout . Element >
140
162
< article >
@@ -146,51 +168,57 @@ const CourseOptimizerPage: FC<{ courseId: string }> = ({ courseId }) => {
146
168
< Badge variant = "dark" className = "ml-2" > { intl . formatMessage ( messages . new ) } </ Badge >
147
169
</ div >
148
170
</ div >
149
- < Button
150
- variant = "primary"
151
- size = "md"
171
+ < StatefulButton
152
172
className = "px-4 rounded-0 scan-course-btn"
173
+ labels = { {
174
+ default : intl . formatMessage ( messages . buttonTitle ) ,
175
+ pending : intl . formatMessage ( messages . buttonTitle ) ,
176
+ } }
177
+ icons = { {
178
+ default : '' ,
179
+ pending : < Spinner
180
+ animation = "border"
181
+ size = "sm"
182
+ className = "mr-2 spinner-icon"
183
+ /> ,
184
+ } }
185
+ state = { getScanButtonState ( ) }
153
186
onClick = { ( ) => dispatch ( startLinkCheck ( courseId ) ) }
154
187
disabled = { ! ! ( linkCheckInProgress ) && ! errorMessage }
155
- >
156
- { linkCheckInProgress && ! errorMessage ? (
157
- < >
158
- < Spinner
159
- animation = "border"
160
- size = "sm"
161
- className = "mr-2 spinner-icon"
162
- />
163
- { intl . formatMessage ( messages . buttonTitle ) }
164
- </ >
165
- ) : (
166
- intl . formatMessage ( messages . buttonTitle )
167
- ) }
168
- </ Button >
188
+ variant = "primary"
189
+ data-testid = "scan-course"
190
+ />
169
191
</ div >
170
192
< Card className = "scan-card" >
171
193
< p className = "px-3 py-1 small" > { intl . formatMessage ( messages . description ) } </ p >
172
194
< hr />
195
+ { showStepper && (
196
+ < Card . Section className = "px-3 py-1" >
197
+ < CourseStepper
198
+ // @ts -ignore
199
+ steps = { courseStepperSteps }
200
+ // @ts -ignore
201
+ activeKey = { currentStage }
202
+ hasError = { currentStage === 1 && ! ! errorMessage }
203
+ errorMessage = { errorMessage }
204
+ />
205
+ </ Card . Section >
206
+ ) }
173
207
< Card . Header
174
208
className = "scan-header h3 px-3 text-black mb-2"
175
209
title = { intl . formatMessage ( messages . scanHeader ) }
176
210
/>
177
211
< Card . Section className = "px-3 py-1" >
178
212
< p className = "small" > { lastScannedAt && `${ intl . formatMessage ( messages . lastScannedOn ) } ${ intl . formatDate ( lastScannedAt , { year : 'numeric' , month : 'long' , day : 'numeric' } ) } ` } </ p >
179
213
</ Card . Section >
180
- { showStepper && (
181
- < Card . Section className = "px-3 py-1" >
182
- < CourseStepper
183
- // @ts -ignore
184
- steps = { courseStepperSteps }
185
- // @ts -ignore
186
- activeKey = { currentStage }
187
- hasError = { currentStage === 1 && ! ! errorMessage }
188
- errorMessage = { errorMessage }
189
- />
190
- </ Card . Section >
191
- ) }
192
214
</ Card >
193
- { ( linkCheckPresent && linkCheckResult ) && < ScanResults data = { linkCheckResult } /> }
215
+ { linkCheckPresent && linkCheckResult && (
216
+ < ScanResults
217
+ data = { linkCheckResult }
218
+ courseId = { courseId }
219
+ onErrorStateChange = { setScanResultsError }
220
+ />
221
+ ) }
194
222
</ article >
195
223
</ Layout . Element >
196
224
</ Layout >
0 commit comments