Files
authsec_reactbootstrapnew/src/components/Dashboard/UserGroupMaintenance.js

769 lines
25 KiB
JavaScript
Raw Normal View History

2025-04-01 20:28:04 +05:30
import React, { useState, useEffect } from "react";
import {
Button,
Dropdown,
Modal,
Form,
Row,
Col,
InputGroup,
FormControl,
} from "react-bootstrap";
import { Table, Pagination, PaginationItem, PaginationLink } from "reactstrap";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
faEdit,
faTrashAlt,
faPlus,
faBars,
faTimes,
faFileExcel,
faUpload,
faDownload,
} from "@fortawesome/free-solid-svg-icons";
import "bootstrap/dist/css/bootstrap.min.css";
import "../Dashboard/CSS/CSS/CommonStyle.css";
import { FaSearch } from "react-icons/fa";
import { BsJournals } from "react-icons/bs";
import { toast } from "react-toastify";
import Spinner from "../../UIComponants/Spinner";
import { Tooltip, OverlayTrigger } from "react-bootstrap";
import * as XLSX from "xlsx";
import { fetchUserGroups } from "../../APIServices/UserGroupMaintenanceApi";
import ExcelControlAPI from "../../APIServices/ExcelControlApi";
import { createUserGroup, deleteUserGroup, updateUserGroup} from "../../APIServices/UserGroupMaintenanceApi";
export function UserGroupMaintenance() {
const [userGroups, setUserGroups] = useState([]);
const [showAddItemPopup, setShowAddItemPopup] = useState(false);
const [currentPage, setCurrentPage] = useState(1);
const [searchQuery, setSearchQuery] = useState("");
const [loading, setLoading] = useState(true);
const [showModal, setShowModal] = useState(false);
const [excelData, setExcelData] = useState([]);
const [newGroupData, setNewGroupData] = useState({
usrGrp: "",
groupName: "",
groupDesc: "",
status: "",
groupLevel: "",
});
const [recordsPerPage, setRecordsPerPage] = useState(10);
const [visibleColumns, setVisibleColumns] = useState({
usrGrp: true,
groupName: true,
groupDesc: true,
status: true,
groupLevel: true,
});
const [isEditing, setIsEditing] = useState(false);
// useEffect(() => {
// const apiUrl = `${process.env.REACT_APP_API_URL}/api/getAllUsrGrp`;
// const token = localStorage.getItem("authToken");
// const fetchUserGroups = async () => {
// try {
// const response = await fetch(apiUrl, {
// method: "GET",
// headers: {
// "Content-Type": "application/json",
// Authorization: `Bearer ${token}`,
// },
// });
// if (!response.ok) {
// throw new Error(`HTTP error! status: ${response.status}`);
// }
// const data = await response.json();
// setUserGroups(data);
// } catch (error) {
// console.error("Fetching error:", error);
// }
// };
// fetchUserGroups();
// }, []);
useEffect(() => {
const loadUserGroups = async () => {
try {
const data = await fetchUserGroups();
setUserGroups(data);
console.log("all user group data ", data);
} catch (error) {
toast.error("Failed to load user groups.");
} finally {
setLoading(false);
}
};
loadUserGroups();
}, []);
useEffect(() => {
// Simulate loading data
setTimeout(() => {
setLoading(false);
}, 3000); // Simulated 3 seconds loading
}, []);
const handleOpenModal = () => setShowModal(true);
const handleCloseModal = () => setShowModal(false);
const handleFileChange = (event) => {
const file = event.target.files[0];
if (file) {
console.log("Selected file:", file.name); // For debugging or processing
}
};
const handleUpload = () => {
// Simulate file upload success
setTimeout(() => {
toast.success("File uploaded successfully!"); // Show toaster notification
setShowModal(false); // Close modal
}, 1000);
};
const exportToExcel = async (fileType) => {
try {
// Fetch binary Excel data
const response = await ExcelControlAPI.demoDownload(fileType);
// Log the response size for debugging
const dataSize = response.data?.byteLength || response.data?.length;
console.log("Response Data Size:", dataSize);
// If file is too large, show error and exit
const MAX_FILE_SIZE = 50 * 1024 * 1024; // 50MB
if (dataSize > MAX_FILE_SIZE) {
console.error("File is too large to process");
return;
}
// Convert binary data to a Blob
const blob = new Blob([response.data], {
type: "application/vnd.ms-excel",
});
// Use FileReader to convert Blob to ArrayBuffer
const arrayBuffer = await new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = reject;
reader.readAsArrayBuffer(blob);
});
console.log("ArrayBuffer Size:", arrayBuffer.byteLength);
// Parse Excel data
const workbook = XLSX.read(arrayBuffer, { type: "array" });
console.log("workbook", workbook);
const firstSheetName = workbook.SheetNames[0];
const worksheet = workbook.Sheets[firstSheetName];
// Convert worksheet to JSON
const jsonData = XLSX.utils.sheet_to_json(worksheet);
console.log("Parsed JSON Data:", jsonData);
// Create a new workbook and trigger download
const newWorksheet = XLSX.utils.json_to_sheet(jsonData);
const newWorkbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(newWorkbook, newWorksheet, fileType);
XLSX.writeFile(newWorkbook, `${fileType}_processed.xlsx`);
console.log("Excel file generated successfully!");
} catch (error) {
console.error("Error exporting to Excel:", error);
}
};
const toggleColumn = (column) => {
setVisibleColumns((prev) => ({
...prev,
[column]: !prev[column],
}));
};
const handleAddItem = () => {
setIsEditing(false);
setNewGroupData({
usrGrp: "",
groupName: "",
groupDesc: "",
status: "",
groupLevel: "",
});
setShowAddItemPopup(true);
};
const handleInputChange = (event) => {
const { name, value } = event.target;
setNewGroupData({ ...newGroupData, [name]: value });
};
const handleSubmit = async (event) => {
event.preventDefault();
try {
// Validate input fields
if (
!newGroupData.usrGrp ||
!newGroupData.groupName ||
!newGroupData.groupDesc ||
!newGroupData.status ||
!newGroupData.groupLevel
) {
toast.error("Please make sure all fields are filled out.");
return;
}
if (isEditing) {
// Update existing group data
setUserGroups(
userGroups.map((group) =>
group.usrGrp === newGroupData.usrGrp ? newGroupData : group
)
);
toast.success("Menu access updated successfully!");
} else {
// Make an API call to create a new user group
const newGroup = { ...newGroupData }; // Prepare data for API
const response = await createUserGroup(newGroup); // Call API function
if (response && response.data) {
// Update userGroups state with the newly created group
setUserGroups([...userGroups, response.data]);
toast.success("Menu access added successfully!");
} else {
toast.error("Failed to add menu access.");
}
}
// Close the modal and reset the form
setShowAddItemPopup(false);
setNewGroupData({
usrGrp: "",
groupName: "",
groupDesc: "",
status: "",
groupLevel: "",
});
} catch (error) {
toast.error("There was an error processing your request.");
console.error("Error in handleSubmit:", error); // Log the error for debugging
}
};
const handleSearch = (query) => {
setSearchQuery(query);
};
const handleClose = () => {
setShowAddItemPopup(false); // Close the modal by setting the state to false
};
const handleEdit = (usrGrp) => {
const groupToEdit = userGroups.find((group) => group.usrGrp === usrGrp);
setIsEditing(true);
setNewGroupData(groupToEdit);
setShowAddItemPopup(true);
};
// const handleDelete = (usrGrp) => {
// setUserGroups(userGroups.filter((group) => group.usrGrp !== usrGrp));
// toast.success("Menu Access delet successfully!");
// };
const handleDelete = async (usrGrp) => {
try {
// Call API to delete the user group
const response = await deleteUserGroup(usrGrp);
if (response && response.data) {
// Update state after successful API call
setUserGroups(userGroups.filter((group) => group.usrGrp !== usrGrp));
toast.success("User group deleted successfully!");
}
} catch (error) {
toast.error("Error deleting Menu Access!");
}
};
const handleRecordsPerPageChange = (number) => {
setRecordsPerPage(number);
setCurrentPage(1);
};
const totalPages = Math.ceil(userGroups.length / recordsPerPage);
const handlePageChange = (pageNumber) => {
setCurrentPage(pageNumber);
};
const slicedUserGroup = userGroups
.filter(
(item) =>
item.groupName &&
item.groupName.toLowerCase().includes(searchQuery.toLowerCase())
)
.slice((currentPage - 1) * recordsPerPage, currentPage * recordsPerPage);
return (
<div style={{ marginTop: "11rem" }}>
{loading ? (
<Spinner />
) : (
<div className="container-fluid mt-5">
<div className="d-flex justify-content-between align-items-center mb-4">
<h1 className="title_main">User Group Maintenance</h1>
</div>
<Row className="align-items-center my-3">
{/* Left: Search Bar */}
<Col
xs={12}
md={8}
lg={6}
className="d-flex justify-content-center my-3"
>
<InputGroup
className="search-bar"
style={{
borderRadius: "10px",
overflow: "hidden",
boxShadow: "0px 4px 12px rgba(0, 0, 0, 0.1)",
width: "100%",
maxWidth: "528px", // Set max-width to limit overall width
paddingRight: "-15px", // Increase padding on the right side
}}
>
<InputGroup.Text
style={{
backgroundColor: "#0E6591",
color: "#fff",
padding: "10px 15px",
}}
>
<FaSearch />
</InputGroup.Text>
<FormControl
placeholder="Search"
value={searchQuery}
onChange={(e) => handleSearch(e.target.value)}
style={{
padding: "10px",
border: "none",
paddingRight: "5px", // More space on the right side of input field
}}
/>
</InputGroup>
</Col>
{/*Add Icons */}
<Col xs={12} md={4} lg={6} className="d-flex justify-content-end">
<>
<OverlayTrigger
placement="bottom"
overlay={<Tooltip>Add Group</Tooltip>}
>
<FontAwesomeIcon
icon={faPlus}
onClick={() => handleAddItem(true)}
style={{
cursor: "pointer",
fontSize: "1.5rem",
color: "#747264",
marginRight: "20px",
}}
/>
</OverlayTrigger>
<OverlayTrigger
placement="bottom"
overlay={<Tooltip>Download template</Tooltip>}
>
<FontAwesomeIcon
icon={faDownload}
style={{
cursor: "pointer",
fontSize: "1.5rem",
color: "#747264",
marginRight: "20px",
}}
/>
</OverlayTrigger>
<OverlayTrigger
placement="bottom"
overlay={<Tooltip>Import</Tooltip>}
>
<span style={{ cursor: "pointer" }}>
<FontAwesomeIcon
icon={faUpload}
style={{
fontSize: "1.5rem",
color: "#747264", // Color for the icon
marginRight: "20px",
}}
onClick={handleOpenModal} // Open modal on click
/>
</span>
</OverlayTrigger>
<OverlayTrigger
placement="bottom"
overlay={<Tooltip>XLSX</Tooltip>}
>
<FontAwesomeIcon
icon={faFileExcel}
onClick={() => exportToExcel("customer")}
style={{
cursor: "pointer",
fontSize: "1.5rem",
color: "#747264",
marginRight: "20px",
}}
/>
</OverlayTrigger>
<OverlayTrigger
placement="bottom"
overlay={<Tooltip>Menu</Tooltip>}
>
<FontAwesomeIcon
icon={faBars}
style={{
cursor: "pointer",
fontSize: "1.5rem",
color: "#747264",
}}
/>
</OverlayTrigger>
{/* Add Icon */}
</>
</Col>
</Row>
<Modal show={showModal} onHide={handleCloseModal} centered>
<Modal.Header closeButton>
<Modal.Title>Import File</Modal.Title>
</Modal.Header>
<Modal.Body>
<Form>
<Form.Group controlId="formFile" className="mb-3">
<Form.Label>Select a file to import:</Form.Label>
<Form.Control
type="file"
accept=".xlsx, .xls, .csv" // Restrict file types
onChange={handleFileChange} // Handle file selection
/>
</Form.Group>
</Form>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleCloseModal}>
Cancel
</Button>
<Button variant="primary" onClick={handleUpload}>
Upload
</Button>
</Modal.Footer>
</Modal>
<div className="table-responsive">
<Table
striped
hover
responsive
align="middle"
className="table-flush shadow-sm"
>
<thead className="custom_header">
<tr>
{Object.keys(visibleColumns).map((key) => (
<th
key={key}
style={{ display: visibleColumns[key] ? "" : "none" }}
>
{key.charAt(0).toUpperCase() + key.slice(1).toLowerCase()}
</th>
))}
<th>Actions</th>
</tr>
</thead>
<tbody className="tbody">
{slicedUserGroup.length === 0 ? (
<tr>
<td
colSpan={
Object.keys(visibleColumns).filter(
(key) => visibleColumns[key]
).length + 1 // +1 for Actions column
}
className="text-center"
>
No data available
</td>
</tr>
) : (
slicedUserGroup.map((group, index) => (
<tr key={index}>
{Object.keys(visibleColumns).map((key) =>
visibleColumns[key] ? (
<td key={key}>
{key === "status" ? (
// Render status dynamically based on isActive or status key
<span
className="status"
style={{
color:
group.status === "Enabled"
? "green"
: "red",
backgroundColor:
group.status === "Enabled"
? "#d4f7d4"
: "#f7d4d4",
padding: "4px 8px",
borderRadius: "4px",
display: "inline-block",
}}
>
{group.status === "Enabled"
? "Active"
: "Inactive"}
</span>
) : (
group[key]
)}
</td>
) : null
)}
<td>
{/* Adding Action icons */}
<FontAwesomeIcon
icon={faEdit}
onClick={() => handleEdit(group.usrGrp)}
style={{
cursor: "pointer",
fontSize: "1rem",
color: "green",
marginRight: "15px",
}}
/>
<FontAwesomeIcon
icon={faTrashAlt}
onClick={() => handleDelete(group.usrGrp)}
style={{
cursor: "pointer",
fontSize: "1rem",
color: "#dc3545",
}}
/>
</td>
</tr>
))
)}
</tbody>
</Table>
</div>
{/* Manage Columns & Records Per Page */}
<Row className="mt-4">
<Col md={6} className="d-flex justify-content-start">
<Dropdown>
<Dropdown.Toggle
variant="outline-primary"
className="custom_manage_column_button"
>
Manage Columns
</Dropdown.Toggle>
<Dropdown.Menu>
{Object.keys(visibleColumns).map((column) => (
<Dropdown.Item
key={column}
onClick={() => toggleColumn(column)}
>
<Form.Check
type="checkbox"
label={
column.charAt(0).toUpperCase() +
column.slice(1).toLowerCase()
}
checked={visibleColumns[column]}
readOnly
className="custom-checkbox"
/>
</Dropdown.Item>
))}
</Dropdown.Menu>
</Dropdown>
</Col>
<Col md={6} className="d-flex justify-content-end">
<Dropdown>
<Dropdown.Toggle
variant="outline-primary"
className="custom_manage_column_button px-4 py-2 border-2 rounded-3 shadow-sm"
id="dropdown-custom-components"
>
<BsJournals />
</Dropdown.Toggle>
<Dropdown.Menu
className="border-0 rounded-3 shadow-lg"
align="end"
>
{[1, 5, 10, 20, 50].map((number) => (
<Dropdown.Item
key={number}
onClick={() => handleRecordsPerPageChange(number)}
className="text-dark d-flex justify-content-between align-items-center"
>
<span>{number}</span>
<i
className="fa fa-check-circle"
style={{
display:
recordsPerPage === number ? "inline" : "none",
}}
/>
</Dropdown.Item>
))}
</Dropdown.Menu>
</Dropdown>
</Col>
</Row>
<Pagination className="pagination">
<PaginationItem disabled={currentPage === 1}>
<PaginationLink
previous
onClick={() => handlePageChange(currentPage - 1)}
/>
</PaginationItem>
{[...Array(totalPages)].map((_, index) => (
<PaginationItem key={index} active={index + 1 === currentPage}>
<PaginationLink
onClick={() => handlePageChange(index + 1)}
style={{ color: "#0b6592" }}
>
{index + 1}
</PaginationLink>
</PaginationItem>
))}
<PaginationItem disabled={currentPage === totalPages}>
<PaginationLink
next
onClick={() => handlePageChange(currentPage + 1)}
/>
</PaginationItem>
</Pagination>
{/* Add/Edit model */}
<Modal
show={showAddItemPopup}
onHide={() => setShowAddItemPopup(false)}
>
<Modal.Header>
<Modal.Title>
{isEditing ? "Edit Group" : "Add New Group"}
</Modal.Title>
<FontAwesomeIcon
icon={faTimes}
size="lg"
onClick={handleClose}
style={{
position: "absolute",
top: "25px",
right: "25px",
cursor: "pointer",
}}
/>
</Modal.Header>
<Modal.Body>
<Form onSubmit={handleSubmit}>
<Form.Group controlId="usrGrp">
<Form.Label>User Group</Form.Label>
<Form.Control
type="text"
name="usrGrp"
value={newGroupData.usrGrp}
onChange={handleInputChange}
className="custom-hover-border"
required
/>
</Form.Group>
<Form.Group controlId="groupName">
<Form.Label>Group Name</Form.Label>
<Form.Control
type="text"
name="groupName"
value={newGroupData.groupName}
onChange={handleInputChange}
className="custom-hover-border"
required
/>
</Form.Group>
<Form.Group controlId="groupDesc">
<Form.Label>Description</Form.Label>
<Form.Control
type="text"
name="groupDesc"
value={newGroupData.groupDesc}
onChange={handleInputChange}
className="custom-hover-border"
/>
</Form.Group>
<Form.Group controlId="status">
<Form.Label>Status</Form.Label>
<Form.Control
as="select"
name="status"
value={newGroupData.status}
onChange={handleInputChange}
className="custom-hover-border"
>
<option value="">Select Status</option>
<option value="Enabled">Enabled</option>
<option value="Disabled">Disabled</option>
</Form.Control>
</Form.Group>
<Form.Group
controlId="groupLevel"
className="custom-hover-border"
>
<Form.Label>Group Level</Form.Label>
<Form.Control
type="number"
name="groupLevel"
value={newGroupData.groupLevel}
onChange={handleInputChange}
/>
</Form.Group>
<Modal.Footer>
<Button
variant="primary"
onClick={() => setShowAddItemPopup(false)}
className="custom_button px-4"
>
Close
</Button>
<Button
type="submit"
variant="primary"
className="custom_button px-4"
>
{isEditing ? "Update Group" : "Add Group"}{" "}
</Button>
</Modal.Footer>
</Form>
</Modal.Body>
</Modal>
</div>
)}
</div>
);
}
export default UserGroupMaintenance;