1
1
import React , { useState , useEffect } from "react" ;
2
- import { Container , Grid2 , TextField , Button , CircularProgress , Typography } from "@mui/material" ;
2
+ import { Container , TextField , Button , CircularProgress , Typography , Box , styled } from "@mui/material" ;
3
+ import Stepper from '@mui/material/Stepper' ;
4
+ import Step from '@mui/material/Step' ;
5
+ import StepLabel from '@mui/material/StepLabel' ;
6
+ import StepConnector , { stepConnectorClasses } from '@mui/material/StepConnector' ;
3
7
import { useNavigate } from "react-router-dom" ;
4
8
import api from '../utils/api' ;
5
9
import * as Yup from 'yup' ;
6
10
import { useFormik } from 'formik' ;
7
- import { REDIRECT_REASONS } from './constants/Constants' ;
8
11
import { showToast } from '../utils/toastUtils' ;
12
+ import Check from '@mui/icons-material/Check' ;
13
+ import SettingsIcon from '@mui/icons-material/Settings' ;
14
+ import GroupAddIcon from '@mui/icons-material/GroupAdd' ;
15
+ import TitleIcon from '@mui/icons-material/Title' ;
16
+
17
+ // Custom connector styling
18
+ const ColorlibConnector = styled ( StepConnector ) ( ( { theme } ) => ( {
19
+ [ `&.${ stepConnectorClasses . alternativeLabel } ` ] : {
20
+ top : 22 ,
21
+ } ,
22
+ [ `&.${ stepConnectorClasses . active } ` ] : {
23
+ [ `& .${ stepConnectorClasses . line } ` ] : {
24
+ backgroundImage : 'linear-gradient(95deg, #2196f3 0%, #1976d2 100%)' ,
25
+ } ,
26
+ } ,
27
+ [ `&.${ stepConnectorClasses . completed } ` ] : {
28
+ [ `& .${ stepConnectorClasses . line } ` ] : {
29
+ backgroundImage : 'linear-gradient(95deg, #2196f3 0%, #1976d2 100%)' ,
30
+ } ,
31
+ } ,
32
+ [ `& .${ stepConnectorClasses . line } ` ] : {
33
+ height : 3 ,
34
+ border : 0 ,
35
+ backgroundColor : theme . palette . mode === 'dark' ? theme . palette . grey [ 800 ] : '#eaeaf0' ,
36
+ borderRadius : 1 ,
37
+ } ,
38
+ } ) ) ;
39
+
40
+ // Custom step icon styling
41
+ const ColorlibStepIconRoot = styled ( 'div' ) ( ( { theme, ownerState } ) => ( {
42
+ backgroundColor : theme . palette . mode === 'dark' ? theme . palette . grey [ 700 ] : '#ccc' ,
43
+ zIndex : 1 ,
44
+ color : '#fff' ,
45
+ width : 50 ,
46
+ height : 50 ,
47
+ display : 'flex' ,
48
+ borderRadius : '50%' ,
49
+ justifyContent : 'center' ,
50
+ alignItems : 'center' ,
51
+ ...( ownerState . active && {
52
+ backgroundImage : 'linear-gradient(136deg, #2196f3 0%, #1976d2 100%)' ,
53
+ boxShadow : '0 4px 10px 0 rgba(0,0,0,.25)' ,
54
+ } ) ,
55
+ ...( ownerState . completed && {
56
+ backgroundImage : 'linear-gradient(136deg, #2196f3 0%, #1976d2 100%)' ,
57
+ } ) ,
58
+ } ) ) ;
59
+
60
+ // Custom step icon component
61
+ function ColorlibStepIcon ( props ) {
62
+ const { active, completed, className } = props ;
63
+
64
+ const icons = {
65
+ 1 : < SettingsIcon /> ,
66
+ 2 : < GroupAddIcon /> ,
67
+ 3 : < TitleIcon /> ,
68
+ } ;
69
+
70
+ return (
71
+ < ColorlibStepIconRoot ownerState = { { completed, active } } className = { className } >
72
+ { completed ? < Check /> : icons [ String ( props . icon ) ] }
73
+ </ ColorlibStepIconRoot >
74
+ ) ;
75
+ }
9
76
10
77
function SetupWizard ( ) {
11
78
const [ currentStep , setCurrentStep ] = useState ( 0 ) ;
@@ -22,7 +89,6 @@ function SetupWizard() {
22
89
23
90
setSetupStatus ( statusData ) ;
24
91
25
- // Update currentStep based on setup status
26
92
if ( ! statusData . database_configured ) {
27
93
setCurrentStep ( 0 ) ;
28
94
} else if ( ! statusData . superuser_exists ) {
@@ -31,9 +97,7 @@ function SetupWizard() {
31
97
setCurrentStep ( 2 ) ;
32
98
}
33
99
} catch ( error ) {
34
- // Handle the case where the status code is 503
35
100
if ( error . response && error . response . status === 503 ) {
36
- // Setup is incomplete, determine which step to show
37
101
const statusData = error . response . data ;
38
102
setSetupStatus ( statusData ) ;
39
103
if ( ! statusData . database_configured ) {
@@ -44,11 +108,9 @@ function SetupWizard() {
44
108
setCurrentStep ( 2 ) ;
45
109
}
46
110
} else {
47
- // Handle other errors or show a generic error message
48
111
showToast ( 'Failed to fetch setup status.' , 'error' ) ;
49
112
}
50
113
} finally {
51
- // Ensure loading is stopped and status is marked as fetched
52
114
setIsLoading ( false ) ;
53
115
setStatusFetched ( true ) ;
54
116
}
@@ -63,11 +125,11 @@ function SetupWizard() {
63
125
title : "Database Configuration" ,
64
126
description : "Enter your database connection details." ,
65
127
fields : [
66
- { id : "db_host" , label : "Database Host" , type : "text" , required : true } ,
67
- { id : "db_port" , label : "Database Port" , type : "number" , required : true } ,
68
- { id : "db_name" , label : "Database Name" , type : "text" , required : true } ,
69
- { id : "db_user" , label : "Database User" , type : "text" , required : true } ,
70
- { id : "db_password" , label : "Database Password" , type : "password" , required : true }
128
+ { id : "db_host" , label : "Database Host" , type : "text" , required : true } ,
129
+ { id : "db_port" , label : "Database Port" , type : "number" , required : true } ,
130
+ { id : "db_name" , label : "Database Name" , type : "text" , required : true } ,
131
+ { id : "db_user" , label : "Database User" , type : "text" , required : true } ,
132
+ { id : "db_password" , label : "Database Password" , type : "password" , required : true }
71
133
] ,
72
134
validationSchema : Yup . object ( {
73
135
db_host : Yup . string ( ) . required ( "Required" ) ,
@@ -82,10 +144,10 @@ function SetupWizard() {
82
144
title : "Admin User" ,
83
145
description : "Create an admin user for the application (if one doesn't exist)." ,
84
146
fields : [
85
- { id : "admin_username" , label : "Admin Username" , type : "text" , required : true } ,
86
- { id : "admin_email" , label : "Admin Email" , type : "email" , required : true } ,
87
- { id : "admin_password" , label : "Admin Password" , type : "password" , required : true } ,
88
- { id : "admin_password2" , label : "Confirm Password" , type : "password" , required : true }
147
+ { id : "admin_username" , label : "Admin Username" , type : "text" , required : true } ,
148
+ { id : "admin_email" , label : "Admin Email" , type : "email" , required : true } ,
149
+ { id : "admin_password" , label : "Admin Password" , type : "password" , required : true } ,
150
+ { id : "admin_password2" , label : "Confirm Password" , type : "password" , required : true }
89
151
] ,
90
152
validationSchema : Yup . object ( {
91
153
admin_username : Yup . string ( ) . required ( "Required" ) ,
@@ -202,27 +264,42 @@ function SetupWizard() {
202
264
203
265
if ( ! statusFetched ) {
204
266
return (
205
- < div className = "text- center mt-5 ">
267
+ < Box display = "flex" justifyContent = " center" alignItems = "center" minHeight = "100vh ">
206
268
< CircularProgress />
207
- </ div >
269
+ </ Box >
208
270
) ;
209
271
}
210
272
211
273
if ( isLoading ) {
212
- return < CircularProgress /> ;
274
+ return (
275
+ < Box display = "flex" justifyContent = "center" alignItems = "center" minHeight = "100vh" >
276
+ < CircularProgress />
277
+ </ Box >
278
+ ) ;
213
279
}
214
280
215
281
return (
216
282
< Container >
217
- < Grid2 container justifyContent = "center" >
218
- < Grid2 item xs = { 12 } md = { 8 } lg = { 6 } >
283
+ < Box sx = { { width : '100%' , mt : 4 } } >
284
+ < Stepper alternativeLabel activeStep = { currentStep } connector = { < ColorlibConnector /> } >
285
+ { steps . map ( ( step ) => (
286
+ < Step key = { step . id } >
287
+ < StepLabel StepIconComponent = { ColorlibStepIcon } > { step . title } </ StepLabel >
288
+ </ Step >
289
+ ) ) }
290
+ </ Stepper >
291
+
292
+ < Box sx = { { mt : 4 , mb : 2 } } >
219
293
< Typography variant = "h4" component = "h2" gutterBottom >
220
294
{ steps [ currentStep ] . title }
221
295
</ Typography >
222
296
< Typography variant = "body1" gutterBottom >
223
297
{ steps [ currentStep ] . description }
224
298
</ Typography >
225
- < form onSubmit = { formik . handleSubmit } noValidate >
299
+ </ Box >
300
+
301
+ < form onSubmit = { formik . handleSubmit } noValidate >
302
+ < Box sx = { { mb : 4 } } >
226
303
{ steps [ currentStep ] . fields . map ( field => (
227
304
< TextField
228
305
key = { field . id }
@@ -236,36 +313,40 @@ function SetupWizard() {
236
313
fullWidth
237
314
margin = "normal"
238
315
error = { formik . errors [ field . id ] && formik . touched [ field . id ] }
239
- helperText = { formik . errors [ field . id ] }
240
- required = { field . required } // Add this line
316
+ helperText = { formik . touched [ field . id ] && formik . errors [ field . id ] }
317
+ required = { field . required }
241
318
/>
242
319
) ) }
243
- < div style = { { display : 'flex' , justifyContent : 'space-between' , alignItems : 'center' } } >
244
- { currentStep > 0 && (
245
- < Button
246
- type = "button"
247
- onClick = { ( ) => setCurrentStep ( step => step - 1 ) }
248
- style = { { marginRight : '10px' } }
249
- variant = "outlined"
250
- >
251
- Back
252
- </ Button >
253
- ) }
320
+ </ Box >
321
+
322
+ < Box sx = { { display : 'flex' , justifyContent : 'space-between' , mt : 3 } } >
323
+ { currentStep > 0 && (
254
324
< Button
255
- type = "submit"
256
- variant = "contained"
257
- color = "primary"
258
- disabled = { isLoading }
259
- startIcon = { isLoading ? < CircularProgress size = "1rem" /> : null }
325
+ variant = "outlined"
326
+ onClick = { ( ) => setCurrentStep ( step => step - 1 ) }
327
+ sx = { { mr : 1 } }
260
328
>
261
- { isLoading ? < CircularProgress size = "1rem" /> : ( currentStep < steps . length - 1 ? "Next" : "Finish" ) }
329
+ Back
262
330
</ Button >
263
- </ div >
264
- </ form >
265
- </ Grid2 >
266
- </ Grid2 >
331
+ ) }
332
+ < Button
333
+ type = "submit"
334
+ variant = "contained"
335
+ color = "primary"
336
+ disabled = { isLoading }
337
+ sx = { { ml : 'auto' } }
338
+ >
339
+ { isLoading ? (
340
+ < CircularProgress size = { 24 } />
341
+ ) : (
342
+ currentStep < steps . length - 1 ? "Next" : "Finish"
343
+ ) }
344
+ </ Button >
345
+ </ Box >
346
+ </ form >
347
+ </ Box >
267
348
</ Container >
268
349
) ;
269
350
}
270
351
271
- export default SetupWizard ;
352
+ export default SetupWizard ;
0 commit comments