// ** React Imports
import { useEffect, useState } from 'react'
// ** MUI Imports
import Box, { BoxProps } from '@mui/material/Box'
import Button from '@mui/material/Button'
import Drawer from '@mui/material/Drawer'
import FormControl from '@mui/material/FormControl'
import FormHelperText from '@mui/material/FormHelperText'
import IconButton from '@mui/material/IconButton'
import InputLabel from '@mui/material/InputLabel'
import MenuItem from '@mui/material/MenuItem'
import Select from '@mui/material/Select'
import { styled } from '@mui/material/styles'
import TextField from '@mui/material/TextField'
import Typography from '@mui/material/Typography'
// ** Third Party Imports
import { yupResolver } from '@hookform/resolvers/yup'
import { Controller, useForm } from 'react-hook-form'
import * as yup from 'yup'
// ** Icon Imports
import Icon from 'src/@core/components/icon'
// ** Store Imports
import { useDispatch, useSelector } from 'react-redux'
// ** Actions Imports
import { addUser } from 'src/store/apps/user'
// ** Types Imports
import { AppDispatch, RootState } from 'src/store'
import { fetchDesignation } from 'src/store/apps/designation'
import { fetchRole } from 'src/store/apps/role'
interface SidebarAddUserType {
open: boolean
toggle: () => void
}
interface UserData {
firstName: string
middleName: string
lastName: string
loginId: string
emailId: string
mobileNumber: string
}
const showErrors = (field: string, valueLen: number, min: number) => {
if (valueLen === 0) {
return `${field} field is required`
} else if (valueLen > 0 && valueLen < min) {
return `${field} must be at least ${min} characters`
} else {
return ''
}
}
const Header = styled(Box)<BoxProps>(({ theme }) => ({
display: 'flex',
alignItems: 'center',
padding: theme.spacing(3, 4),
justifyContent: 'space-between',
backgroundColor: theme.palette.background.default
}))
const schema = yup.object().shape({
emailId: yup.string().email().required(),
mobileNumber: yup
.string()
.typeError('Mobile Number field is required')
// .min(10, obj => showErrors('Contact Number', obj.value.length, obj.min))
.required(),
firstName: yup
.string()
// .min(3, obj => showErrors('First Name', obj.value.length, obj.min))
.required(),
middleName: yup.string(),
// .min(3, obj => showErrors('First Name', obj.value.length, obj.min))
lastName: yup
.string()
// .min(3, obj => showErrors('First Name', obj.value.length, obj.min))
.required(),
loginId: yup
.string()
// .min(3, obj => showErrors('Username', obj.value.length, obj.min))
.required()
})
const defaultValues = {
firstName: '',
middleName: '',
lastName: '',
loginId: '',
emailId: '',
mobileNumber: ''
}
const SidebarAddUser = (props: SidebarAddUserType) => {
// ** Props
const { open, toggle } = props
// ** State
const [plan, setPlan] = useState<string>('basic')
const [roleId, setRoleId] = useState<string>('')
const [reportsTo, setReporterTo] = useState<string>('')
const [departmentId, setDepartmentId] = useState<string>('')
const [designationId, setDesignationId] = useState<string>('')
// ** Hooks
const dispatch = useDispatch<AppDispatch>()
const store = useSelector((state: RootState) => state.role)
const designations = useSelector((state: RootState) => state.designation)
useEffect(() => {
dispatch(fetchRole({}))
dispatch(fetchDesignation({}))
}, [])
const {
reset,
control,
setValue,
handleSubmit,
formState: { errors }
} = useForm({
defaultValues,
mode: 'onChange',
resolver: yupResolver(schema)
})
const onSubmit = (data: UserData) => {
console.log({ ...data, roleId, reportsTo, departmentId, designationId })
dispatch(addUser({ ...data, roleId, reportsTo, departmentId, designationId }))
toggle()
reset()
}
const handleClose = () => {
setPlan('basic')
// setRole('subscriber')
// setValue('contact', Number(''))
toggle()
reset()
}
return (
<Drawer
open={open}
anchor='right'
variant='temporary'
onClose={handleClose}
ModalProps={{ keepMounted: true }}
sx={{ '& .MuiDrawer-paper': { width: { xs: 600, sm: 450 } } }}
>
<Header>
<Typography variant='h6'>Add User</Typography>
<IconButton size='small' onClick={handleClose} sx={{ color: 'text.primary' }}>
<Icon icon='mdi:close' fontSize={20} />
</IconButton>
</Header>
<Box sx={{ p: 5 }}>
<form onSubmit={handleSubmit(onSubmit)}>
<FormControl fullWidth sx={{ mb: 6 }}>
<Controller
name='firstName'
control={control}
rules={{ required: true }}
render={({ field: { value, onChange } }) => (
<TextField
value={value}
label='First Name'
onChange={onChange}
placeholder='John Doe'
error={Boolean(errors.firstName)}
/>
)}
/>
{errors.firstName && (
<FormHelperText sx={{ color: 'error.main' }}>{errors.firstName.message}</FormHelperText>
)}
</FormControl>
<FormControl fullWidth sx={{ mb: 6 }}>
<Controller
name='middleName'
control={control}
rules={{ required: true }}
render={({ field: { value, onChange } }) => (
<TextField
value={value}
label='Middle Name'
onChange={onChange}
placeholder='johndoe'
error={Boolean(errors.middleName)}
/>
)}
/>
{errors.middleName && (
<FormHelperText sx={{ color: 'error.main' }}>{errors.middleName.message}</FormHelperText>
)}
</FormControl>
<FormControl fullWidth sx={{ mb: 6 }}>
<Controller
name='lastName'
control={control}
rules={{ required: true }}
render={({ field: { value, onChange } }) => (
<TextField
value={value}
label='Last Name'
onChange={onChange}
placeholder=''
error={Boolean(errors.lastName)}
/>
)}
/>
{errors.lastName && <FormHelperText sx={{ color: 'error.main' }}>{errors.lastName.message}</FormHelperText>}
</FormControl>
<FormControl fullWidth sx={{ mb: 6 }}>
<Controller
name='loginId'
control={control}
rules={{ required: true }}
render={({ field: { value, onChange } }) => (
<TextField
type='text'
value={value}
label='Login ID'
onChange={onChange}
placeholder='123456'
error={Boolean(errors.loginId)}
/>
)}
/>
{errors.loginId && <FormHelperText sx={{ color: 'error.main' }}>{errors.loginId.message}</FormHelperText>}
</FormControl>
<FormControl fullWidth sx={{ mb: 6 }}>
<Controller
name='emailId'
control={control}
rules={{ required: true }}
render={({ field: { value, onChange } }) => (
<TextField
value={value}
label='Email Id'
onChange={onChange}
placeholder='[email protected]'
error={Boolean(errors.emailId)}
/>
)}
/>
{errors.emailId && <FormHelperText sx={{ color: 'error.main' }}>{errors.emailId.message}</FormHelperText>}
</FormControl>
<FormControl fullWidth sx={{ mb: 6 }}>
<Controller
name='mobileNumber'
control={control}
rules={{ required: true }}
render={({ field: { value, onChange } }) => (
<TextField
value={value}
label='Mobile Number'
onChange={onChange}
placeholder='9874561230'
error={Boolean(errors.mobileNumber)}
/>
)}
/>
{errors.mobileNumber && (
<FormHelperText sx={{ color: 'error.main' }}>{errors.mobileNumber.message}</FormHelperText>
)}
</FormControl>
<FormControl fullWidth sx={{ mb: 6 }}>
<InputLabel id='department-select'>Department</InputLabel>
<Select
fullWidth
value={departmentId}
id='select-department'
label='Select Department'
labelId='department-select'
onChange={e => setDepartmentId(e.target.value)}
inputProps={{ placeholder: 'Select Department' }}
>
<MenuItem value=''>
<em>Choose a Department</em>
</MenuItem>
{store.role.map(p => (
<MenuItem key={p.id} value={p.roleName}>
{p.roleName}
</MenuItem>
))}
</Select>
{/* {errors.departmentId && (
<FormHelperText sx={{ color: 'error.main' }}>{errors.departmentId.message}</FormHelperText>
)} */}
</FormControl>
<FormControl fullWidth sx={{ mb: 6 }}>
<InputLabel id='designation-select'>Designation</InputLabel>
<Select
fullWidth
value={designationId}
id='select-designation'
label='Select Designation'
labelId='designation-select'
onChange={e => setDesignationId(e.target.value)}
inputProps={{ placeholder: 'Select Designation' }}
>
<MenuItem value=''>
<em>Choose a Designation</em>
</MenuItem>
{designations.allDesignations.map(p => (
<MenuItem key={p.key} value={p.key}>
{p.value}
</MenuItem>
))}
</Select>
{/* {errors.designationId && (
<FormHelperText sx={{ color: 'error.main' }}>{errors.designationId.message}</FormHelperText>
)} */}
</FormControl>
<FormControl fullWidth sx={{ mb: 6 }}>
<InputLabel id='reportsTo-select'>Reporter</InputLabel>
<Select
fullWidth
value={reportsTo}
id='select-reporter'
label='Select Reporter'
labelId='reportsTo-select'
onChange={e => setReporterTo(e.target.value)}
inputProps={{ placeholder: 'Select Reporter' }}
>
<MenuItem value={1}>Reporter1</MenuItem>
<MenuItem value={2}>Reporter2</MenuItem>
<MenuItem value={3}>Reporter3</MenuItem>
<MenuItem value={4}>Reporter4</MenuItem>
</Select>
</FormControl>
<FormControl fullWidth sx={{ mb: 6 }}>
<InputLabel id='role-select'>Role</InputLabel>
<Select
fullWidth
value={roleId}
labelId='role-select'
inputProps={{ placeholder: 'Select Reporter' }}
id='select-role'
label='Select Role'
onChange={e => setRoleId(e.target.value)}
>
<MenuItem value=''>
<em>Choose a Role</em>
</MenuItem>
{store.role.map(p => (
<MenuItem key={p.id} value={p.roleName}>
{p.roleName}
</MenuItem>
))}
</Select>
</FormControl>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Button size='large' type='submit' variant='contained' sx={{ mr: 3 }}>
Submit
</Button>
<Button size='large' variant='outlined' color='secondary' onClick={handleClose}>
Cancel
</Button>
</Box>
</form>
</Box>
</Drawer>
)
}
export default SidebarAddUser
The id, rolename which you are seeing in red lines are coming from the api data. I am not getting the thing that where I need to specify that the id and rolename is coming from the api. The same is happening for all the select elements. All the dropdowns are dynamic where, and id and rolename are coming from the api. Where I will specify the interface for the rolename, id, value, key which are dynamic
I need to know where I need to specify the interface for the api result data.
The file where the RootState is defined.
// ** Toolkit imports
import { configureStore } from '@reduxjs/toolkit'
// ** Reducers
import calendar from 'src/store/apps/calendar'
import chat from 'src/store/apps/chat'
import designation from 'src/store/apps/designation'
import email from 'src/store/apps/email'
import invoice from 'src/store/apps/invoice'
import permissions from 'src/store/apps/permissions'
import role from 'src/store/apps/role'
import user from 'src/store/apps/user'
export const store = configureStore({
reducer: {
user,
role,
designation,
chat,
email,
invoice,
calendar,
permissions
},
middleware: getDefaultMiddleware =>
getDefaultMiddleware({
serializableCheck: false
})
})
export type AppDispatch = typeof store.dispatch
export type RootState = ReturnType<typeof store.getState>
src/store/apps/role file. where the actions are written
// ** Redux Imports
import {
createAsyncThunk,
createSlice
} from '@reduxjs/toolkit'
import {
Dispatch
} from 'redux'
// ** Axios Imports
import axios from 'axios'
interface RoleParams {
// id: number
// roleName: string
// description: string
// ipAddress: string
// createdOn: string
// updatedOn: string
// createdBy: string
// updatedBy: string
}
interface Redux {
getState: any
dispatch: Dispatch < any >
}
const demo_token =
'xzy'
// ** Fetch Users
export const fetchRole = createAsyncThunk('appRoles/fetchRole', async(params: RoleParams) => {
try {
const response = await axios.get('http://consoleapi.xzy.com/api/Roles', {
headers: {
Authorization: `Bearer ${demo_token}`
}
})
console.log(response.data)
return response.data
} catch (error) {
console.error(error)
}
})
// ** Add User
// export const addUser = createAsyncThunk(
// 'appUsers/addUser',
// async (data: { [key: string]: number | string }, { getState, dispatch }: Redux) => {
// const response = await axios.post(
// 'http://consoleapi.xzy.com/api/Account/AddUser',
// {
// data
// },
// {
// headers: {
// Authorization: `Bearer ${demo_token}`
// }
// }
// )
// dispatch(fetchData(getState().user.params))
// return response.data
// }
// )
// ** Delete User
// export const deleteUser = createAsyncThunk(
// 'appUsers/deleteUser',
// async (id: number | string, { getState, dispatch }: Redux) => {
// const response = await axios.delete('/apps/users/delete', {
// data: id
// })
// dispatch(fetchData(getState().user.params))
// return response.data
// }
// )
export const appRolesSlice = createSlice({
name: 'appRoles',
initialState: {
role: [],
allRole: []
},
reducers: {},
extraReducers: builder => {
builder.addCase(fetchRole.fulfilled, (state, action) => {
state.role = action.payload.data
// state.total = action.payload.total
// state.params = action.payload.params
state.allRole = action.payload.data
})
}
})
export default appRolesSlice.reducer
CodePudding user response:
When you define an array in typescript, without any types or inferred types, it assumes the type of the array items to be never
. So, you will have to give some type to your array.
interface Role {
id: string;
roleName: string;
}
export const appRolesSlice = createSlice({
...
initialState: {
role: [] as Array<Role>,
allRole: []
},
...
})
You need to do the same for allRole
property.