dev-jaanvi #1
|
@ -21,7 +21,8 @@ interface AddEditCategoryModalProps {
|
||||||
name: string,
|
name: string,
|
||||||
email: string,
|
email: string,
|
||||||
phone: string,
|
phone: string,
|
||||||
registeredAddress: string
|
registeredAddress: string,
|
||||||
|
password: string
|
||||||
) => void;
|
) => void;
|
||||||
editRow: any;
|
editRow: any;
|
||||||
}
|
}
|
||||||
|
@ -31,6 +32,7 @@ interface FormData {
|
||||||
email: string;
|
email: string;
|
||||||
phone: string;
|
phone: string;
|
||||||
registeredAddress: string;
|
registeredAddress: string;
|
||||||
|
password: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AddEditCategoryModal: React.FC<AddEditCategoryModalProps> = ({
|
const AddEditCategoryModal: React.FC<AddEditCategoryModalProps> = ({
|
||||||
|
@ -52,6 +54,7 @@ const AddEditCategoryModal: React.FC<AddEditCategoryModalProps> = ({
|
||||||
email: "",
|
email: "",
|
||||||
phone: "",
|
phone: "",
|
||||||
registeredAddress: "",
|
registeredAddress: "",
|
||||||
|
password: "",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -62,7 +65,8 @@ const AddEditCategoryModal: React.FC<AddEditCategoryModalProps> = ({
|
||||||
data.name,
|
data.name,
|
||||||
data.email,
|
data.email,
|
||||||
data.phone,
|
data.phone,
|
||||||
data.registeredAddress
|
data.registeredAddress,
|
||||||
|
data.password
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
handleCreate(data);
|
handleCreate(data);
|
||||||
|
@ -168,7 +172,26 @@ const AddEditCategoryModal: React.FC<AddEditCategoryModalProps> = ({
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
<Controller
|
||||||
|
name="password"
|
||||||
|
control={control}
|
||||||
|
rules={{
|
||||||
|
required: "password is required",
|
||||||
|
}}
|
||||||
|
render={({ field }) => (
|
||||||
|
<TextField
|
||||||
|
{...field}
|
||||||
|
required
|
||||||
|
margin="dense"
|
||||||
|
label="Password"
|
||||||
|
type="password"
|
||||||
|
fullWidth
|
||||||
|
variant="standard"
|
||||||
|
error={!!errors.password}
|
||||||
|
helperText={errors.password?.message}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
<Controller
|
<Controller
|
||||||
name="phone"
|
name="phone"
|
||||||
control={control}
|
control={control}
|
||||||
|
|
300
src/components/AddEditRoleModal/index.tsx
Normal file
300
src/components/AddEditRoleModal/index.tsx
Normal file
|
@ -0,0 +1,300 @@
|
||||||
|
import React, { useEffect } from "react";
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Checkbox,
|
||||||
|
Dialog,
|
||||||
|
DialogActions,
|
||||||
|
DialogContent,
|
||||||
|
DialogTitle,
|
||||||
|
FormControl,
|
||||||
|
FormControlLabel,
|
||||||
|
FormHelperText,
|
||||||
|
FormLabel,
|
||||||
|
InputLabel,
|
||||||
|
MenuItem,
|
||||||
|
Select,
|
||||||
|
TextField,
|
||||||
|
} from "@mui/material";
|
||||||
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
|
import { useForm, Controller } from "react-hook-form";
|
||||||
|
|
||||||
|
interface AddRoleModalProps {
|
||||||
|
open: boolean;
|
||||||
|
handleClose: () => void;
|
||||||
|
handleCreate: (data: FormData) => void;
|
||||||
|
handleUpdate: (
|
||||||
|
id: string,
|
||||||
|
name: string,
|
||||||
|
resource: {
|
||||||
|
moduleName: string;
|
||||||
|
moduleId: string;
|
||||||
|
permissions: string[];
|
||||||
|
}[]
|
||||||
|
) => void;
|
||||||
|
editRow: any;
|
||||||
|
data: {
|
||||||
|
resource: {
|
||||||
|
moduleName: string;
|
||||||
|
moduleId: string;
|
||||||
|
permissions: string[];
|
||||||
|
}[];
|
||||||
|
}; // Assuming `data` is passed as a prop
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FormData {
|
||||||
|
name: string;
|
||||||
|
resource: {
|
||||||
|
moduleName: string;
|
||||||
|
moduleId: string;
|
||||||
|
permissions: string[];
|
||||||
|
}[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const AddRoleModal: React.FC<AddRoleModalProps> = ({
|
||||||
|
open,
|
||||||
|
handleClose,
|
||||||
|
handleCreate,
|
||||||
|
handleUpdate,
|
||||||
|
editRow,
|
||||||
|
data,
|
||||||
|
}) => {
|
||||||
|
const {
|
||||||
|
control,
|
||||||
|
handleSubmit,
|
||||||
|
formState: { errors },
|
||||||
|
setValue,
|
||||||
|
reset,
|
||||||
|
getValues, // Access getValues from the form methods here
|
||||||
|
} = useForm<FormData>({
|
||||||
|
defaultValues: {
|
||||||
|
name: "",
|
||||||
|
resource: [], // Ensure resource is initialized as an empty array
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (editRow) {
|
||||||
|
setValue("name", editRow.name);
|
||||||
|
setValue("resource", editRow.resource);
|
||||||
|
}
|
||||||
|
}, [editRow, setValue]);
|
||||||
|
|
||||||
|
// Handles permissions checkbox change for a specific resource
|
||||||
|
const handlePermissionChange = (
|
||||||
|
resourceIndex: number,
|
||||||
|
permission: string,
|
||||||
|
checked: boolean
|
||||||
|
) => {
|
||||||
|
const updatedResources = [...getValues().resource]; // Use getValues to get the current form values
|
||||||
|
const resource = updatedResources[resourceIndex];
|
||||||
|
|
||||||
|
if (checked) {
|
||||||
|
// Add permission if checked
|
||||||
|
resource.permissions = [
|
||||||
|
...new Set([...resource.permissions, permission]),
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
// Remove permission if unchecked
|
||||||
|
resource.permissions = resource.permissions.filter(
|
||||||
|
(p) => p !== permission
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
setValue("resource", updatedResources); // Update the resource field in form state
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSubmit = (data: FormData) => {
|
||||||
|
if (editRow) {
|
||||||
|
handleUpdate(editRow.id, data.name, data.resource);
|
||||||
|
} else {
|
||||||
|
handleCreate(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleClose();
|
||||||
|
reset();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
open={open}
|
||||||
|
onClose={handleClose}
|
||||||
|
maxWidth="md"
|
||||||
|
fullWidth
|
||||||
|
PaperProps={{
|
||||||
|
component: "form",
|
||||||
|
onSubmit: handleSubmit(onSubmit),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DialogTitle
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{editRow ? "Edit Role" : "Add Role"}
|
||||||
|
<Box
|
||||||
|
onClick={handleClose}
|
||||||
|
sx={{
|
||||||
|
cursor: "pointer",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CloseIcon />
|
||||||
|
</Box>
|
||||||
|
</DialogTitle>
|
||||||
|
|
||||||
|
<DialogContent>
|
||||||
|
{/* Role Name Field */}
|
||||||
|
<Controller
|
||||||
|
name="name"
|
||||||
|
control={control}
|
||||||
|
rules={{
|
||||||
|
required: "Role Name is required",
|
||||||
|
minLength: {
|
||||||
|
value: 3,
|
||||||
|
message: "Minimum 3 characters required",
|
||||||
|
},
|
||||||
|
maxLength: {
|
||||||
|
value: 30,
|
||||||
|
message: "Maximum 30 characters allowed",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
render={({ field }) => (
|
||||||
|
<TextField
|
||||||
|
{...field}
|
||||||
|
autoFocus
|
||||||
|
required
|
||||||
|
margin="dense"
|
||||||
|
label="Role Name"
|
||||||
|
type="text"
|
||||||
|
fullWidth
|
||||||
|
variant="standard"
|
||||||
|
error={!!errors.name}
|
||||||
|
helperText={errors.name?.message}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Resource Field */}
|
||||||
|
<Controller
|
||||||
|
name="resource"
|
||||||
|
control={control}
|
||||||
|
rules={{ required: "Resource is required" }}
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormControl
|
||||||
|
fullWidth
|
||||||
|
margin="dense"
|
||||||
|
error={!!errors.resource}
|
||||||
|
>
|
||||||
|
<InputLabel>Resource</InputLabel>
|
||||||
|
<Select
|
||||||
|
{...field}
|
||||||
|
label="Resource"
|
||||||
|
required
|
||||||
|
fullWidth
|
||||||
|
variant="standard"
|
||||||
|
>
|
||||||
|
{/* Mapping over the resource array to display the options */}
|
||||||
|
{data.resource?.map((resourceItem, index) => (
|
||||||
|
<MenuItem
|
||||||
|
key={index}
|
||||||
|
value={resourceItem.moduleId}
|
||||||
|
>
|
||||||
|
{resourceItem.moduleName}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
<FormHelperText>
|
||||||
|
{errors.resource?.message}
|
||||||
|
</FormHelperText>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Permissions Checkbox Fields for each resource */}
|
||||||
|
{getValues().resource &&
|
||||||
|
getValues().resource.length > 0 &&
|
||||||
|
getValues().resource.map((resource, resourceIndex) => (
|
||||||
|
<React.Fragment key={resourceIndex}>
|
||||||
|
<FormControl fullWidth margin="dense">
|
||||||
|
<FormLabel>
|
||||||
|
{resource.moduleName} Permissions
|
||||||
|
</FormLabel>
|
||||||
|
<FormControlLabel
|
||||||
|
control={
|
||||||
|
<Checkbox
|
||||||
|
value="view"
|
||||||
|
checked={resource.permissions.includes(
|
||||||
|
"view"
|
||||||
|
)}
|
||||||
|
onChange={(e) =>
|
||||||
|
handlePermissionChange(
|
||||||
|
resourceIndex,
|
||||||
|
"view",
|
||||||
|
e.target.checked
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label="View"
|
||||||
|
/>
|
||||||
|
<FormControlLabel
|
||||||
|
control={
|
||||||
|
<Checkbox
|
||||||
|
value="edit"
|
||||||
|
checked={resource.permissions.includes(
|
||||||
|
"edit"
|
||||||
|
)}
|
||||||
|
onChange={(e) =>
|
||||||
|
handlePermissionChange(
|
||||||
|
resourceIndex,
|
||||||
|
"edit",
|
||||||
|
e.target.checked
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label="Edit"
|
||||||
|
/>
|
||||||
|
<FormControlLabel
|
||||||
|
control={
|
||||||
|
<Checkbox
|
||||||
|
value="delete"
|
||||||
|
checked={resource.permissions.includes(
|
||||||
|
"delete"
|
||||||
|
)}
|
||||||
|
onChange={(e) =>
|
||||||
|
handlePermissionChange(
|
||||||
|
resourceIndex,
|
||||||
|
"delete",
|
||||||
|
e.target.checked
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label="Delete"
|
||||||
|
/>
|
||||||
|
<FormHelperText>
|
||||||
|
{
|
||||||
|
errors.resource?.[resourceIndex]
|
||||||
|
?.permissions?.message
|
||||||
|
}
|
||||||
|
</FormHelperText>
|
||||||
|
</FormControl>
|
||||||
|
</React.Fragment>
|
||||||
|
))}
|
||||||
|
</DialogContent>
|
||||||
|
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={handleClose}>Cancel</Button>
|
||||||
|
<Button type="submit">{editRow ? "Update" : "Create"}</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AddRoleModal;
|
212
src/components/AddUserModel/index.tsx
Normal file
212
src/components/AddUserModel/index.tsx
Normal file
|
@ -0,0 +1,212 @@
|
||||||
|
import React, { useEffect } from "react";
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Dialog,
|
||||||
|
DialogActions,
|
||||||
|
DialogContent,
|
||||||
|
DialogTitle,
|
||||||
|
TextField,
|
||||||
|
} from "@mui/material";
|
||||||
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
|
import { useForm, Controller } from "react-hook-form";
|
||||||
|
|
||||||
|
//By Jaanvi : Edit Model :: 11-feb-25
|
||||||
|
interface AddUserModalProps {
|
||||||
|
open: boolean;
|
||||||
|
handleClose: () => void;
|
||||||
|
handleCreate: (data: FormData) => void;
|
||||||
|
handleUpdate: (
|
||||||
|
id: string,
|
||||||
|
name: string,
|
||||||
|
email: string,
|
||||||
|
phone: string,
|
||||||
|
password: string
|
||||||
|
) => void;
|
||||||
|
editRow: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FormData {
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
phone: string;
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
const AddUserModal: React.FC<AddUserModalProps> = ({
|
||||||
|
open,
|
||||||
|
handleClose,
|
||||||
|
handleCreate,
|
||||||
|
|
||||||
|
editRow,
|
||||||
|
}) => {
|
||||||
|
const {
|
||||||
|
control,
|
||||||
|
handleSubmit,
|
||||||
|
formState: { errors },
|
||||||
|
setValue,
|
||||||
|
reset,
|
||||||
|
} = useForm<FormData>({
|
||||||
|
defaultValues: {
|
||||||
|
name: "",
|
||||||
|
email: "",
|
||||||
|
phone: "",
|
||||||
|
password: "",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const onSubmit = (data: FormData) => {
|
||||||
|
handleCreate(data);
|
||||||
|
|
||||||
|
handleClose();
|
||||||
|
reset();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
open={open}
|
||||||
|
onClose={handleClose}
|
||||||
|
maxWidth="md"
|
||||||
|
fullWidth
|
||||||
|
PaperProps={{
|
||||||
|
component: "form",
|
||||||
|
onSubmit: handleSubmit(onSubmit),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DialogTitle
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{editRow ? "Edit Admin" : "Add Admin"}
|
||||||
|
<Box
|
||||||
|
onClick={handleClose}
|
||||||
|
sx={{
|
||||||
|
cursor: "pointer",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CloseIcon />
|
||||||
|
</Box>
|
||||||
|
</DialogTitle>
|
||||||
|
|
||||||
|
<DialogContent>
|
||||||
|
<Controller
|
||||||
|
name="name"
|
||||||
|
control={control}
|
||||||
|
rules={{
|
||||||
|
required: "Admin Name is required",
|
||||||
|
minLength: {
|
||||||
|
value: 3,
|
||||||
|
message: "Minimum 3 characters required",
|
||||||
|
},
|
||||||
|
maxLength: {
|
||||||
|
value: 30,
|
||||||
|
message: "Maximum 30 characters allowed",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
render={({ field }) => (
|
||||||
|
<TextField
|
||||||
|
{...field}
|
||||||
|
autoFocus
|
||||||
|
required
|
||||||
|
margin="dense"
|
||||||
|
label="User Name"
|
||||||
|
type="text"
|
||||||
|
fullWidth
|
||||||
|
variant="standard"
|
||||||
|
error={!!errors.name}
|
||||||
|
helperText={errors.name?.message}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Controller
|
||||||
|
name="email"
|
||||||
|
control={control}
|
||||||
|
rules={{
|
||||||
|
required: "Email is required",
|
||||||
|
pattern: {
|
||||||
|
value: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
|
||||||
|
message: "Invalid email address",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
render={({ field }) => (
|
||||||
|
<TextField
|
||||||
|
{...field}
|
||||||
|
required
|
||||||
|
margin="dense"
|
||||||
|
label="Email"
|
||||||
|
type="email"
|
||||||
|
fullWidth
|
||||||
|
variant="standard"
|
||||||
|
error={!!errors.email}
|
||||||
|
helperText={errors.email?.message}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
name="password"
|
||||||
|
control={control}
|
||||||
|
rules={{
|
||||||
|
required: "password is required",
|
||||||
|
}}
|
||||||
|
render={({ field }) => (
|
||||||
|
<TextField
|
||||||
|
{...field}
|
||||||
|
required
|
||||||
|
margin="dense"
|
||||||
|
label="Password"
|
||||||
|
type="password"
|
||||||
|
fullWidth
|
||||||
|
variant="standard"
|
||||||
|
error={!!errors.password}
|
||||||
|
helperText={errors.password?.message}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
name="phone"
|
||||||
|
control={control}
|
||||||
|
rules={{
|
||||||
|
required: "Phone number is required",
|
||||||
|
pattern: {
|
||||||
|
value: /^[0-9]*$/,
|
||||||
|
message: "Only numbers are allowed",
|
||||||
|
},
|
||||||
|
minLength: {
|
||||||
|
value: 6,
|
||||||
|
message: "Phone number must be exactly 6 digits",
|
||||||
|
},
|
||||||
|
maxLength: {
|
||||||
|
value: 14,
|
||||||
|
message: "Phone number must be exactly 14 digits",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
render={({ field }) => (
|
||||||
|
<TextField
|
||||||
|
{...field}
|
||||||
|
required
|
||||||
|
margin="dense"
|
||||||
|
label="Phone Number"
|
||||||
|
type="tel"
|
||||||
|
fullWidth
|
||||||
|
variant="standard"
|
||||||
|
error={!!errors.phone}
|
||||||
|
helperText={errors.phone?.message}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</DialogContent>
|
||||||
|
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={handleClose}>Cancel</Button>
|
||||||
|
<Button type="submit">Create</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AddUserModal;
|
|
@ -29,6 +29,11 @@ const baseMenuItems = [
|
||||||
icon: <AnalyticsRoundedIcon />,
|
icon: <AnalyticsRoundedIcon />,
|
||||||
url: "/panel/user-list",
|
url: "/panel/user-list",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
text: "Roles",
|
||||||
|
icon: <AnalyticsRoundedIcon />,
|
||||||
|
url: "/panel/role-list",
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
//Eknoor singh and Jaanvi
|
//Eknoor singh and Jaanvi
|
||||||
|
@ -44,7 +49,7 @@ type PropType = {
|
||||||
export default function MenuContent({ hidden }: PropType) {
|
export default function MenuContent({ hidden }: PropType) {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const userRole = useSelector(
|
const userRole = useSelector(
|
||||||
(state: RootState) => state.profileReducer.user?.role
|
(state: RootState) => state.profileReducer.user?.userType
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
137
src/pages/RoleList/index.tsx
Normal file
137
src/pages/RoleList/index.tsx
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { Box, Button, Typography } from "@mui/material";
|
||||||
|
import AddEditRoleModal from "../../components/AddEditRoleModal";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
import CustomTable, { Column } from "../../components/CustomTable";
|
||||||
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
import { createRole, roleList } from "../../redux/slices/roleSlice";
|
||||||
|
import { AppDispatch, RootState } from "../../redux/store/store";
|
||||||
|
|
||||||
|
export default function RoleList() {
|
||||||
|
const [modalOpen, setModalOpen] = useState(false);
|
||||||
|
const { reset } = useForm();
|
||||||
|
|
||||||
|
const [deleteModal, setDeleteModal] = React.useState<boolean>(false);
|
||||||
|
const [viewModal, setViewModal] = React.useState<boolean>(false);
|
||||||
|
const [rowData, setRowData] = React.useState<any | null>(null);
|
||||||
|
|
||||||
|
const dispatch = useDispatch<AppDispatch>();
|
||||||
|
|
||||||
|
const roles = useSelector((state: RootState) => state.roleReducer.roles);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch(roleList());
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
|
const handleClickOpen = () => {
|
||||||
|
setRowData(null); // Reset row data when opening for new role
|
||||||
|
setModalOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCloseModal = () => {
|
||||||
|
setModalOpen(false);
|
||||||
|
setRowData(null);
|
||||||
|
reset();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCreate = async (data: {
|
||||||
|
name: string;
|
||||||
|
resource: {
|
||||||
|
moduleName: string;
|
||||||
|
moduleId: string;
|
||||||
|
permissions: string[];
|
||||||
|
}[];
|
||||||
|
}) => {
|
||||||
|
try {
|
||||||
|
await dispatch(createRole(data));
|
||||||
|
await dispatch(roleList()); // Refresh the list after creation
|
||||||
|
handleCloseModal();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Creation failed", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const categoryColumns: Column[] = [
|
||||||
|
{ id: "srno", label: "Sr No" },
|
||||||
|
{ id: "name", label: "Name" },
|
||||||
|
{ id: "action", label: "Action", align: "center" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const categoryRows = roles?.length
|
||||||
|
? roles?.map(function (
|
||||||
|
role: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
// email: string;
|
||||||
|
|
||||||
|
// phone: string;
|
||||||
|
// location?: string;
|
||||||
|
// managerAssigned?: string;
|
||||||
|
// vehicle?: string;
|
||||||
|
},
|
||||||
|
index: number
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
id: role?.id,
|
||||||
|
srno: index + 1,
|
||||||
|
name: role?.name,
|
||||||
|
// email: user?.email,
|
||||||
|
// phone: user?.phone,
|
||||||
|
// location: user?.location,
|
||||||
|
// managerAssigned: user?.managerAssigned,
|
||||||
|
// vehicle: user?.vehicle,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
: [];
|
||||||
|
|
||||||
|
console.log("Category Rows:", categoryRows);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: "100%",
|
||||||
|
maxWidth: {
|
||||||
|
sm: "100%",
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
component="h2"
|
||||||
|
variant="h6"
|
||||||
|
sx={{ mt: 2, fontWeight: 600 }}
|
||||||
|
>
|
||||||
|
Roles
|
||||||
|
</Typography>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
size="medium"
|
||||||
|
sx={{ textAlign: "right" }}
|
||||||
|
onClick={handleClickOpen}
|
||||||
|
>
|
||||||
|
Add Role
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<CustomTable
|
||||||
|
columns={categoryColumns}
|
||||||
|
rows={categoryRows}
|
||||||
|
setDeleteModal={setDeleteModal}
|
||||||
|
deleteModal={deleteModal}
|
||||||
|
setViewModal={setViewModal}
|
||||||
|
viewModal={viewModal}
|
||||||
|
setRowData={setRowData}
|
||||||
|
setModalOpen={setModalOpen}
|
||||||
|
/>
|
||||||
|
<AddEditRoleModal
|
||||||
|
open={modalOpen}
|
||||||
|
handleClose={handleCloseModal}
|
||||||
|
handleCreate={handleCreate}
|
||||||
|
editRow={rowData}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
File diff suppressed because it is too large
Load diff
|
@ -4,12 +4,14 @@ import authReducer from "./slices/authSlice";
|
||||||
import adminReducer from "./slices/adminSlice";
|
import adminReducer from "./slices/adminSlice";
|
||||||
import profileReducer from "./slices/profileSlice";
|
import profileReducer from "./slices/profileSlice";
|
||||||
import userReducer from "./slices/userSlice.ts";
|
import userReducer from "./slices/userSlice.ts";
|
||||||
|
import roleReducer from "./slices/roleSlice.ts";
|
||||||
|
|
||||||
const rootReducer = combineReducers({
|
const rootReducer = combineReducers({
|
||||||
authReducer,
|
authReducer,
|
||||||
adminReducer,
|
adminReducer,
|
||||||
profileReducer,
|
profileReducer,
|
||||||
userReducer,
|
userReducer,
|
||||||
|
roleReducer,
|
||||||
});
|
});
|
||||||
|
|
||||||
export type RootState = ReturnType<typeof rootReducer>;
|
export type RootState = ReturnType<typeof rootReducer>;
|
||||||
|
|
|
@ -7,7 +7,7 @@ interface User {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
email: string;
|
email: string;
|
||||||
role: string;
|
userType: string;
|
||||||
phone: string;
|
phone: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
115
src/redux/slices/roleSlice.ts
Normal file
115
src/redux/slices/roleSlice.ts
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit";
|
||||||
|
import axios from "axios";
|
||||||
|
import http from "../../lib/https";
|
||||||
|
import { toast } from "sonner";
|
||||||
|
|
||||||
|
// Define TypeScript types
|
||||||
|
interface Role {
|
||||||
|
id: any;
|
||||||
|
name: string;
|
||||||
|
resource: {
|
||||||
|
moduleName: string;
|
||||||
|
moduleId: string;
|
||||||
|
permissions: string[];
|
||||||
|
}[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RoleState {
|
||||||
|
roles: Role[];
|
||||||
|
loading: boolean;
|
||||||
|
error: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initial state
|
||||||
|
const initialState: RoleState = {
|
||||||
|
roles: [],
|
||||||
|
loading: false,
|
||||||
|
error: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const roleList = createAsyncThunk<Role[], void, { rejectValue: string }>(
|
||||||
|
"fetchRoles",
|
||||||
|
async (_, { rejectWithValue }) => {
|
||||||
|
try {
|
||||||
|
const token = localStorage?.getItem("authToken");
|
||||||
|
if (!token) throw new Error("No token found");
|
||||||
|
|
||||||
|
const response = await http.get("get");
|
||||||
|
|
||||||
|
if (!response.data?.data) throw new Error("Invalid API response");
|
||||||
|
|
||||||
|
return response.data.data;
|
||||||
|
} catch (error: any) {
|
||||||
|
toast.error("Error Fetching Roles" + error);
|
||||||
|
return rejectWithValue(
|
||||||
|
error?.response?.data?.message || "An error occurred"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create Role
|
||||||
|
export const createRole = createAsyncThunk<
|
||||||
|
Role,
|
||||||
|
{
|
||||||
|
name: string;
|
||||||
|
resource: {
|
||||||
|
moduleName: string;
|
||||||
|
moduleId: string;
|
||||||
|
permissions: string[];
|
||||||
|
}[];
|
||||||
|
},
|
||||||
|
{ rejectValue: string }
|
||||||
|
>("/CreateRole", async (data, { rejectWithValue }) => {
|
||||||
|
try {
|
||||||
|
const response = await http.post("create", data);
|
||||||
|
return response.data;
|
||||||
|
} catch (error: any) {
|
||||||
|
return rejectWithValue(
|
||||||
|
error.response?.data?.message || "An error occurred"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const roleSlice = createSlice({
|
||||||
|
name: "fetchRoles",
|
||||||
|
initialState,
|
||||||
|
reducers: {},
|
||||||
|
extraReducers: (builder) => {
|
||||||
|
builder
|
||||||
|
.addCase(roleList.pending, (state) => {
|
||||||
|
state.loading = true;
|
||||||
|
state.error = null;
|
||||||
|
})
|
||||||
|
.addCase(
|
||||||
|
roleList.fulfilled,
|
||||||
|
(state, action: PayloadAction<any>) => {
|
||||||
|
state.loading = false;
|
||||||
|
state.roles = action.payload.results; // Extract results from response
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.addCase(roleList.rejected, (state, action) => {
|
||||||
|
state.loading = false;
|
||||||
|
state.error = action.payload || "Failed to fetch roles";
|
||||||
|
})
|
||||||
|
.addCase(createRole.pending, (state) => {
|
||||||
|
state.loading = true;
|
||||||
|
})
|
||||||
|
.addCase(
|
||||||
|
createRole.fulfilled,
|
||||||
|
(state, action: PayloadAction<Role>) => {
|
||||||
|
state.loading = false;
|
||||||
|
state.roles.push(action.payload);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.addCase(
|
||||||
|
createRole.rejected,
|
||||||
|
(state, action: PayloadAction<string | undefined>) => {
|
||||||
|
state.loading = false;
|
||||||
|
state.error = action.payload || "Failed to create role";
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default roleSlice.reducer;
|
|
@ -1,5 +1,7 @@
|
||||||
import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit";
|
import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import http from "../../lib/https";
|
||||||
|
import { toast } from "sonner";
|
||||||
|
|
||||||
// Define TypeScript types
|
// Define TypeScript types
|
||||||
interface User {
|
interface User {
|
||||||
|
@ -7,9 +9,10 @@ interface User {
|
||||||
name: string;
|
name: string;
|
||||||
email: string;
|
email: string;
|
||||||
phone?: string;
|
phone?: string;
|
||||||
location?: string;
|
// location?: string;
|
||||||
managerAssigned?: string;
|
// managerAssigned?: string;
|
||||||
vehicle?: string;
|
// vehicle?: string;
|
||||||
|
password:string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UserState {
|
interface UserState {
|
||||||
|
@ -26,17 +29,62 @@ const initialState: UserState = {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Async thunk to fetch user list
|
// Async thunk to fetch user list
|
||||||
export const userList = createAsyncThunk<User[]>("users/fetchUsers", async () => {
|
// export const userList = createAsyncThunk<User[]>("users/fetchUsers", async () => {
|
||||||
|
// try {
|
||||||
|
// const response = await axios.get<User[]>("/api/users"); // Adjust the API endpoint as needed
|
||||||
|
// return response.data;
|
||||||
|
// } catch (error: any) {
|
||||||
|
// throw new Error(error.response?.data?.message || "Failed to fetch users");
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
export const userList = createAsyncThunk<User, void, { rejectValue: string }>(
|
||||||
|
"fetchUsers",
|
||||||
|
async (_, { rejectWithValue }) => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get<User[]>("/api/users"); // Adjust the API endpoint as needed
|
const token = localStorage?.getItem("authToken");
|
||||||
|
if (!token) throw new Error("No token found");
|
||||||
|
|
||||||
|
const response = await http.get("users-list");
|
||||||
|
|
||||||
|
if (!response.data?.data) throw new Error("Invalid API response");
|
||||||
|
|
||||||
|
return response.data.data;
|
||||||
|
} catch (error: any) {
|
||||||
|
toast.error("Error Fetching Profile" + error);
|
||||||
|
return rejectWithValue(
|
||||||
|
error?.response?.data?.message || "An error occurred"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
//Create User
|
||||||
|
export const createUser = createAsyncThunk<
|
||||||
|
User,
|
||||||
|
{
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
password: string;
|
||||||
|
phone: string;
|
||||||
|
|
||||||
|
// location?: string;
|
||||||
|
// managerAssigned?: string;
|
||||||
|
// vehicle?: string;
|
||||||
|
},
|
||||||
|
{ rejectValue: string }
|
||||||
|
>("/CreateUser", async (data, { rejectWithValue }) => {
|
||||||
|
try {
|
||||||
|
const response = await http.post("create-user", data);
|
||||||
return response.data;
|
return response.data;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
throw new Error(error.response?.data?.message || "Failed to fetch users");
|
return rejectWithValue(
|
||||||
|
error.response?.data?.message || "An error occurred"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const userSlice = createSlice({
|
const userSlice = createSlice({
|
||||||
name: "users",
|
name: "fetchUsers",
|
||||||
initialState,
|
initialState,
|
||||||
reducers: {},
|
reducers: {},
|
||||||
extraReducers: (builder) => {
|
extraReducers: (builder) => {
|
||||||
|
@ -45,14 +93,34 @@ const userSlice = createSlice({
|
||||||
state.loading = true;
|
state.loading = true;
|
||||||
state.error = null;
|
state.error = null;
|
||||||
})
|
})
|
||||||
.addCase(userList.fulfilled, (state, action: PayloadAction<User[]>) => {
|
.addCase(
|
||||||
|
userList.fulfilled,
|
||||||
|
(state, action: PayloadAction<User[]>) => {
|
||||||
state.loading = false;
|
state.loading = false;
|
||||||
state.users = action.payload;
|
state.users = action.payload;
|
||||||
})
|
}
|
||||||
|
)
|
||||||
.addCase(userList.rejected, (state, action) => {
|
.addCase(userList.rejected, (state, action) => {
|
||||||
state.loading = false;
|
state.loading = false;
|
||||||
state.error = action.error.message || "Failed to fetch users";
|
state.error = action.error.message || "Failed to fetch users";
|
||||||
});
|
})
|
||||||
|
.addCase(createUser.pending, (state) => {
|
||||||
|
state.loading = true;
|
||||||
|
// state.error = null;
|
||||||
|
})
|
||||||
|
.addCase(
|
||||||
|
createUser.fulfilled,
|
||||||
|
(state, action: PayloadAction<User>) => {
|
||||||
|
state.loading = false;
|
||||||
|
state.users.push(action.payload);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.addCase(
|
||||||
|
createUser.rejected,
|
||||||
|
(state, action: PayloadAction<string | undefined>) => {
|
||||||
|
state.loading = false;
|
||||||
|
}
|
||||||
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { Routes as BaseRoutes, Navigate, Route } from "react-router-dom";
|
||||||
import React, { lazy, Suspense } from "react";
|
import React, { lazy, Suspense } from "react";
|
||||||
import LoadingComponent from "./components/Loading";
|
import LoadingComponent from "./components/Loading";
|
||||||
import DashboardLayout from "./layouts/DashboardLayout";
|
import DashboardLayout from "./layouts/DashboardLayout";
|
||||||
|
import RoleList from "./pages/RoleList";
|
||||||
|
|
||||||
// Page imports
|
// Page imports
|
||||||
const Login = lazy(() => import("./pages/Auth/Login"));
|
const Login = lazy(() => import("./pages/Auth/Login"));
|
||||||
|
@ -83,6 +84,15 @@ export default function AppRouter() {
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
<Route
|
||||||
|
path="role-list"
|
||||||
|
element={
|
||||||
|
<ProtectedRoute
|
||||||
|
caps={[]}
|
||||||
|
component={<RoleList />}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
<Route
|
<Route
|
||||||
path="profile"
|
path="profile"
|
||||||
|
|
Loading…
Reference in a new issue