first commit
This commit is contained in:
BIN
src/components/BuilderComponants/.DS_Store
vendored
Normal file
BIN
src/components/BuilderComponants/.DS_Store
vendored
Normal file
Binary file not shown.
602
src/components/Dashboard/APIRegistry.js
Normal file
602
src/components/Dashboard/APIRegistry.js
Normal file
@@ -0,0 +1,602 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import {
|
||||
Button,
|
||||
Dropdown,
|
||||
Modal,
|
||||
Form,
|
||||
Row,
|
||||
Col,
|
||||
InputGroup,
|
||||
FormControl,
|
||||
} from "react-bootstrap";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import {
|
||||
faEdit,
|
||||
faTrashAlt,
|
||||
faPlus,
|
||||
faBars,
|
||||
faTimes,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import "bootstrap/dist/css/bootstrap.min.css";
|
||||
import "../Dashboard/CSS/CSS/CommonStyle.css";
|
||||
import { Table, Pagination, PaginationItem, PaginationLink } from "reactstrap";
|
||||
import { FaSearch, FaTimes } from "react-icons/fa";
|
||||
import { BsJournals } from "react-icons/bs";
|
||||
import { toast } from "react-toastify";
|
||||
import Spinner from '../../UIComponants/Spinner';
|
||||
import { fetchRegistry,addRegistry,updateRegistry,deleteRegistry } from "APIServices/APIRegistryApi";
|
||||
import { set } from "react-hook-form";
|
||||
|
||||
function APIRegistry() {
|
||||
const [apiEntries, setApiEntries] = useState([]);
|
||||
const [showAddEditModal, setShowAddEditModal] = useState(false);
|
||||
const [currentApiEntry, setCurrentApiEntry] = useState({
|
||||
id: "",
|
||||
table_name: "",
|
||||
createdAt: "",
|
||||
updatedAt: "",
|
||||
isActive: false,
|
||||
|
||||
});
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const [recordsPerPage, setRecordsPerPage] = useState(10);
|
||||
const [visibleColumns, setVisibleColumns] = useState({
|
||||
id: true,
|
||||
table_name: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
isActive: true,
|
||||
actions: true,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate loading data
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
}, 3000); // Simulated 3 seconds loading
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
fetchApiEntries();
|
||||
}, []);
|
||||
|
||||
const fetchApiEntries = async () => {
|
||||
const apiUrl = `${process.env.REACT_APP_API_URL}/Api_registery_header/Api_registery_header`;
|
||||
const token = localStorage.getItem("authToken");
|
||||
|
||||
if (!token) {
|
||||
console.error("Authorization token is missing.");
|
||||
return;
|
||||
}
|
||||
|
||||
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();
|
||||
console.log("API entries:", data);
|
||||
setApiEntries(data);
|
||||
toast.success("API entries fetched successfully!");
|
||||
} catch (error) {
|
||||
console.error("Error fetching API entries:", error);
|
||||
toast.error("Failed to fetch API entries.");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const toggleColumn = (column) => {
|
||||
setVisibleColumns((prev) => ({
|
||||
...prev,
|
||||
[column]: !prev[column],
|
||||
}));
|
||||
};
|
||||
|
||||
const handleInputChange = (event) => {
|
||||
const { name, value } = event.target;
|
||||
setCurrentApiEntry((prev) => ({
|
||||
...prev,
|
||||
[name]: value,
|
||||
}));
|
||||
};
|
||||
|
||||
const handleSearch = (query) => {
|
||||
setSearchQuery(query);
|
||||
};
|
||||
|
||||
// const handleSubmit = async (event) => {
|
||||
// event.preventDefault();
|
||||
// try {
|
||||
// if (isEditing) {
|
||||
// // Update existing API entry
|
||||
// setApiEntries(
|
||||
// apiEntries.map((entry) =>
|
||||
// entry.id === currentApiEntry.id ? currentApiEntry : entry
|
||||
// )
|
||||
// );
|
||||
// toast.success("API entry updated successfully!");
|
||||
// } else {
|
||||
// // Add new API entry
|
||||
// const newApiEntry = {
|
||||
// ...currentApiEntry,
|
||||
// id: `ID${apiEntries.length + 1}`, // Generate a new ID
|
||||
// };
|
||||
|
||||
// const response = await addRegistry(newApiEntry);
|
||||
// // const newId = `ID${apiEntries.length + 1}`;
|
||||
// if (response) {
|
||||
// setApiEntries([...apiEntries, response.data]); // Use API response data if needed
|
||||
// toast.success("API added successfully!");
|
||||
// } else {
|
||||
// toast.error("Failed to add API entry.");
|
||||
// }
|
||||
// }
|
||||
|
||||
// setShowAddEditModal(false); // Close modal after submission
|
||||
// } catch (error) {
|
||||
// toast.error("There was an error while submitting the API.");
|
||||
// console.error("Error in handleSubmit:", error); // Log the error for debugging
|
||||
// }
|
||||
|
||||
// };
|
||||
|
||||
const handleSubmit = async (event) => {
|
||||
event.preventDefault();
|
||||
try {
|
||||
if (isEditing) {
|
||||
// Update existing API entry
|
||||
const response = await updateRegistry(currentApiEntry.id, currentApiEntry);
|
||||
if(response){
|
||||
setApiEntries(
|
||||
apiEntries.map((entry) =>
|
||||
entry.id === currentApiEntry.id ? response.data : entry
|
||||
)
|
||||
);
|
||||
toast.success("API entry updated successfully!");
|
||||
} else {
|
||||
toast.error("Failed to update API entry.");
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
// Add new API entry
|
||||
const newApiEntry = {
|
||||
...currentApiEntry,
|
||||
id: `ID${apiEntries.length + 1}`, // Generate a new ID
|
||||
};
|
||||
|
||||
const response = await addRegistry(newApiEntry);
|
||||
if (response) {
|
||||
setApiEntries([...apiEntries, response.data]); // Update state with API response
|
||||
toast.success("API added successfully!");
|
||||
} else {
|
||||
toast.error("Failed to add API entry.");
|
||||
}
|
||||
setApiEntries([...currentApiEntry, ...apiEntries, newApiEntry]);
|
||||
toast.success("API added successfully!");
|
||||
}
|
||||
|
||||
fetchApiEntries(); // Refresh API entries after submit
|
||||
setShowAddEditModal(false); // Close modal
|
||||
} catch (error) {
|
||||
toast.error("There was an error while submitting the API.");
|
||||
console.error("Error in handleSubmit:", error); // Log the error for debugging
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
const openModal = (
|
||||
entry = {
|
||||
id: "",
|
||||
table_name: "",
|
||||
createdAt: "",
|
||||
updatedAt: "",
|
||||
isActive: false,
|
||||
}
|
||||
) => {
|
||||
setIsEditing(!!entry.id);
|
||||
setCurrentApiEntry(entry);
|
||||
setShowAddEditModal(true);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setShowAddEditModal(false); // Close the modal by setting the state to false
|
||||
};
|
||||
|
||||
const handleDelete = async (id) => {
|
||||
|
||||
try {
|
||||
const response = await deleteRegistry(id);
|
||||
if(response){
|
||||
setApiEntries(apiEntries.filter((entry) => entry.id !== id));
|
||||
toast.success("API is deleted successfully....");
|
||||
}else{
|
||||
toast.error("Failed to delete API entry.");
|
||||
}
|
||||
} catch (error) {
|
||||
toast.error("There was an error while deleting the API.");
|
||||
console.error("Error in handleDelete:", error);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const handleRecordsPerPageChange = (number) => {
|
||||
setRecordsPerPage(number);
|
||||
setCurrentPage(1);
|
||||
};
|
||||
|
||||
const totalPages = Math.ceil(apiEntries.length / recordsPerPage);
|
||||
const handlePageChange = (pageNumber) => {
|
||||
setCurrentPage(pageNumber);
|
||||
};
|
||||
|
||||
const slicedAPIEntries = apiEntries
|
||||
.filter(
|
||||
(item) =>
|
||||
item.table_name &&
|
||||
item.table_name.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">API Registry</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">
|
||||
<>
|
||||
{/* Add Icon */}
|
||||
<FontAwesomeIcon
|
||||
icon={faPlus}
|
||||
onClick={() => openModal(true)}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
fontSize: "1.5rem",
|
||||
color: "#747264",
|
||||
marginRight: "20px",
|
||||
}}
|
||||
/>
|
||||
<FontAwesomeIcon
|
||||
icon={faBars}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
fontSize: "1.5rem",
|
||||
color: "#747264",
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<div className="table-responsive">
|
||||
<Table
|
||||
striped
|
||||
hover
|
||||
responsive
|
||||
align="middle"
|
||||
className=" table-flush shadow-sm"
|
||||
>
|
||||
<thead className="custom_header">
|
||||
<tr>
|
||||
{Object.keys(visibleColumns)
|
||||
.filter((key) => visibleColumns[key])
|
||||
.map((key) => (
|
||||
<th key={key}>
|
||||
{key.charAt(0).toUpperCase() + key.slice(1)}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="tbody">
|
||||
{slicedAPIEntries.length === 0 ? (
|
||||
<tr>
|
||||
<td
|
||||
colSpan={
|
||||
Object.keys(visibleColumns).filter(
|
||||
(key) => visibleColumns[key]
|
||||
).length
|
||||
}
|
||||
className="text-center"
|
||||
>
|
||||
No Data Available
|
||||
</td>
|
||||
</tr>
|
||||
) : slicedAPIEntries.length > 0 ? (
|
||||
slicedAPIEntries.map((entry, index) => (
|
||||
<tr key={index}>
|
||||
{Object.keys(visibleColumns)
|
||||
.filter((key) => visibleColumns[key])
|
||||
.map((key) => (
|
||||
<td key={key}>
|
||||
{key === "actions" ? (
|
||||
<div className="">
|
||||
<FontAwesomeIcon
|
||||
icon={faEdit}
|
||||
onClick={() => openModal(entry)}
|
||||
className="me-2"
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
fontSize: "1rem",
|
||||
color: "green", // edit icon color
|
||||
marginRight: "15px", // space between icons
|
||||
}}
|
||||
/>
|
||||
<FontAwesomeIcon
|
||||
icon={faTrashAlt}
|
||||
onClick={() => handleDelete(entry.id)}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
fontSize: "1rem",
|
||||
color: "#dc3545", // delete icon color
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : key === "isActive" ? (
|
||||
<span
|
||||
className="status"
|
||||
style={{
|
||||
color: entry.isActive ? "green" : "red", // Dynamic text color
|
||||
backgroundColor: entry.isActive
|
||||
? "#d4f7d4"
|
||||
: "#f7d4d4", // Faint green or red background
|
||||
padding: "4px 8px", // Add some padding for the background effect
|
||||
borderRadius: "4px", // Rounded corners
|
||||
display: "inline-block", // Ensure the background only affects the text
|
||||
}}
|
||||
>
|
||||
{entry.isActive ? "Active" : "Inactive"}
|
||||
</span>
|
||||
) : (
|
||||
entry[key]
|
||||
)}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))
|
||||
) : (
|
||||
<tr>
|
||||
<td
|
||||
colSpan={Object.keys(visibleColumns).length}
|
||||
className="text-center"
|
||||
>
|
||||
No API entries found. Please add new entries.
|
||||
</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>
|
||||
|
||||
<Modal show={showAddEditModal} onHide={() => setShowAddEditModal(false)}>
|
||||
<Modal.Header>
|
||||
<Modal.Title>
|
||||
{isEditing ? "Edit API Entry" : "Add API Entry"}
|
||||
</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="formTableName">
|
||||
<Form.Label>Table Name</Form.Label>
|
||||
<Form.Control
|
||||
type="text"
|
||||
name="tableName"
|
||||
value={currentApiEntry.table_name}
|
||||
onChange={handleInputChange}
|
||||
required
|
||||
className="custom-hover-border"
|
||||
/>
|
||||
</Form.Group>
|
||||
<Form.Group controlId="formCreatedAt">
|
||||
<Form.Label>Created At</Form.Label>
|
||||
<Form.Control
|
||||
type="text"
|
||||
name="createdAt"
|
||||
value={currentApiEntry.createdAt}
|
||||
onChange={handleInputChange}
|
||||
className="custom-hover-border"
|
||||
/>
|
||||
</Form.Group>
|
||||
<Form.Group controlId="formUpdatedAt">
|
||||
<Form.Label>Updated At</Form.Label>
|
||||
<Form.Control
|
||||
type="text"
|
||||
name="updatedAt"
|
||||
value={currentApiEntry.updatedAt}
|
||||
onChange={handleInputChange}
|
||||
className="custom-hover-border"
|
||||
/>
|
||||
</Form.Group>
|
||||
<Form.Group controlId="formActive">
|
||||
<Form.Check
|
||||
type="checkbox"
|
||||
label="Active?"
|
||||
name="isActive"
|
||||
checked={currentApiEntry.isActive}
|
||||
onChange={handleInputChange}
|
||||
className="custom-checkbox"
|
||||
/>
|
||||
</Form.Group>
|
||||
<Modal.Footer>
|
||||
<Button
|
||||
variant="primary"
|
||||
className="custom_button px-4"
|
||||
onClick={() => setShowAddEditModal(false)}
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
className="custom_button px-4"
|
||||
type="submit"
|
||||
>
|
||||
{isEditing ? "Update" : "Add"}
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Form>
|
||||
</Modal.Body>
|
||||
</Modal>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default APIRegistry;
|
||||
496
src/components/Dashboard/AccessType.js
Normal file
496
src/components/Dashboard/AccessType.js
Normal file
@@ -0,0 +1,496 @@
|
||||
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,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import "bootstrap/dist/css/bootstrap.min.css";
|
||||
import "../Dashboard/CSS/CSS/CommonStyle.css";
|
||||
import { FaSearch, FaTimes } from "react-icons/fa";
|
||||
import { BsJournals } from "react-icons/bs";
|
||||
import { toast } from "react-toastify";
|
||||
import Spinner from '../../UIComponants/Spinner';
|
||||
|
||||
function AccessTypeManagement() {
|
||||
const [accessTypes, setAccessTypes] = useState([]);
|
||||
const [showAddEditModal, setShowAddEditModal] = useState(false);
|
||||
const [currentAccessType, setCurrentAccessType] = useState({
|
||||
typeId: "",
|
||||
typeName: "",
|
||||
description: "",
|
||||
isActive: false,
|
||||
});
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const [recordsPerPage, setRecordsPerPage] = useState(10);
|
||||
const [visibleColumns, setVisibleColumns] = useState({
|
||||
typeId: true,
|
||||
typeName: true,
|
||||
description: true,
|
||||
isActive: true,
|
||||
actions: true,
|
||||
});
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate loading data
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
}, 3000); // Simulated 3 seconds loading
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchAccessTypes = async () => {
|
||||
const apiUrl = `${process.env.REACT_APP_API_URL}/api/getAllAccessTypes`;
|
||||
const token = localStorage.getItem("authToken");
|
||||
|
||||
if (!token) {
|
||||
console.error("Authorization token is missing.");
|
||||
return;
|
||||
}
|
||||
|
||||
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();
|
||||
setAccessTypes(data);
|
||||
} catch (error) {
|
||||
console.error("Error fetching access types:", error);
|
||||
}
|
||||
};
|
||||
|
||||
fetchAccessTypes();
|
||||
}, []);
|
||||
|
||||
const toggleColumn = (column) => {
|
||||
setVisibleColumns((prev) => ({
|
||||
...prev,
|
||||
[column]: !prev[column],
|
||||
}));
|
||||
};
|
||||
|
||||
const handleInputChange = (event) => {
|
||||
const { name, value, checked, type } = event.target;
|
||||
setCurrentAccessType((prev) => ({
|
||||
...prev,
|
||||
[name]: type === "checkbox" ? checked : value,
|
||||
}));
|
||||
};
|
||||
|
||||
const handleSearch = (query) => {
|
||||
setSearchQuery(query);
|
||||
};
|
||||
|
||||
const handleSubmit = (event) => {
|
||||
try {
|
||||
event.preventDefault();
|
||||
if (isEditing) {
|
||||
setAccessTypes(
|
||||
accessTypes.map((type) =>
|
||||
type.typeId === currentAccessType.typeId ? currentAccessType : type
|
||||
)
|
||||
);
|
||||
toast.success("AccessType updated successfully!");
|
||||
} else {
|
||||
const newTypeId = `ID${accessTypes.length + 1}`;
|
||||
setAccessTypes([
|
||||
...accessTypes,
|
||||
{ ...currentAccessType, typeId: newTypeId },
|
||||
]);
|
||||
toast.success("AccessType added successfully!");
|
||||
}
|
||||
setShowAddEditModal(false);
|
||||
} catch (error) {
|
||||
toast.error("There was an error while submitting the Type.");
|
||||
console.error("Error in handleSubmit:", error); // Log the error for debugging
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const openModal = (
|
||||
type = { typeId: "", typeName: "", description: "", isActive: false }
|
||||
) => {
|
||||
setIsEditing(!!type.typeId);
|
||||
setCurrentAccessType(type);
|
||||
setShowAddEditModal(true);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setShowAddEditModal(false); // Close the modal by setting the state to false
|
||||
};
|
||||
|
||||
const slicedAccessTypes = accessTypes
|
||||
.filter(
|
||||
(item) =>
|
||||
item.typeName &&
|
||||
item.typeName.toLowerCase().includes(searchQuery.toLowerCase())
|
||||
)
|
||||
.slice((currentPage - 1) * recordsPerPage, currentPage * recordsPerPage);
|
||||
const handleDelete = (typeId) => {
|
||||
setAccessTypes(accessTypes.filter((type) => type.typeId !== typeId));
|
||||
toast.success("Access Type Deleted ... ");
|
||||
};
|
||||
|
||||
const handleRecordsPerPageChange = (number) => {
|
||||
setRecordsPerPage(number);
|
||||
setCurrentPage(1);
|
||||
};
|
||||
|
||||
const totalPages = Math.ceil(accessTypes.length / recordsPerPage);
|
||||
const handlePageChange = (pageNumber) => {
|
||||
setCurrentPage(pageNumber);
|
||||
};
|
||||
|
||||
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">Access Type</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">
|
||||
<>
|
||||
{/* Add Icon */}
|
||||
<FontAwesomeIcon
|
||||
icon={faPlus}
|
||||
onClick={() => openModal(true)}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
fontSize: "1.5rem",
|
||||
color: "#747264",
|
||||
marginRight: "20px",
|
||||
}}
|
||||
/>
|
||||
<FontAwesomeIcon
|
||||
icon={faBars}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
fontSize: "1.5rem",
|
||||
color: "#747264",
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<div className="table-responsive">
|
||||
<Table striped hover responsive align="middle" className=" table-flush shadow-sm">
|
||||
<thead className="custom_header">
|
||||
<tr>
|
||||
{Object.entries(visibleColumns).map(([key, visible]) =>
|
||||
visible ? (
|
||||
<th key={key}>
|
||||
{key.charAt(0).toUpperCase() + key.slice(1)}
|
||||
</th>
|
||||
) : null
|
||||
)}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="tbody">
|
||||
{slicedAccessTypes.length === 0 ? (
|
||||
<tr>
|
||||
<td
|
||||
colSpan={
|
||||
Object.keys(visibleColumns).filter(
|
||||
(key) => visibleColumns[key]
|
||||
).length
|
||||
}
|
||||
className="text-center"
|
||||
>
|
||||
No Data Available
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
slicedAccessTypes.map((type) => (
|
||||
<tr key={type.typeId}>
|
||||
{Object.entries(visibleColumns).map(([key, visible]) =>
|
||||
visible ? (
|
||||
<td key={key}>
|
||||
{key === "actions" ? (
|
||||
<div className="">
|
||||
<FontAwesomeIcon
|
||||
icon={faEdit}
|
||||
onClick={() => openModal(type)}
|
||||
className="me-2"
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
fontSize: "1rem",
|
||||
color: "green", // edit icon color
|
||||
marginRight: "15px", // space between icons
|
||||
}}
|
||||
/>
|
||||
<FontAwesomeIcon
|
||||
icon={faTrashAlt}
|
||||
onClick={() => handleDelete(type.typeId)}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
fontSize: "1rem",
|
||||
color: "#dc3545", // delete icon color
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : key === "isActive" ? (
|
||||
<span
|
||||
className="status"
|
||||
style={{
|
||||
color: type.isActive ? "green" : "red", // Dynamic text color
|
||||
backgroundColor: type.isActive
|
||||
? "#d4f7d4"
|
||||
: "#f7d4d4", // Faint green or red background
|
||||
padding: "4px 8px", // Add some padding for the background effect
|
||||
borderRadius: "4px", // Rounded corners
|
||||
display: "inline-block", // Ensure the background only affects the text
|
||||
}}
|
||||
>
|
||||
{type.isActive ? "Active" : "Inactive"}
|
||||
</span>
|
||||
) : (
|
||||
type[key]
|
||||
)}
|
||||
</td>
|
||||
) : null
|
||||
)}
|
||||
</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={showAddEditModal} onHide={() => setShowAddEditModal(false)} >
|
||||
<Modal.Header>
|
||||
<Modal.Title>
|
||||
{isEditing ? "Edit Access Type" : "Add Access Type"}
|
||||
</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="formTypeName">
|
||||
<Form.Label>Type Name</Form.Label>
|
||||
<Form.Control
|
||||
type="text"
|
||||
name="typeName"
|
||||
value={currentAccessType.typeName}
|
||||
onChange={handleInputChange}
|
||||
required
|
||||
className="custom-hover-border"
|
||||
/>
|
||||
</Form.Group>
|
||||
<Form.Group controlId="formDescription">
|
||||
<Form.Label>Description</Form.Label>
|
||||
<Form.Control
|
||||
type="text"
|
||||
name="description"
|
||||
value={currentAccessType.description}
|
||||
onChange={handleInputChange}
|
||||
className="custom-hover-border"
|
||||
/>
|
||||
</Form.Group>
|
||||
<Form.Group controlId="formActive">
|
||||
<Form.Check
|
||||
type="checkbox"
|
||||
label="Active?"
|
||||
name="isActive"
|
||||
checked={currentAccessType.isActive}
|
||||
onChange={handleInputChange}
|
||||
className="custom-checkbox"
|
||||
/>
|
||||
</Form.Group>
|
||||
<Modal.Footer>
|
||||
<Button
|
||||
variant="primary"
|
||||
className="custom_button px-4"
|
||||
onClick={() => setShowAddEditModal(false)}
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
className="custom_button px-4"
|
||||
type="submit"
|
||||
>
|
||||
{isEditing ? "Update" : "Add"}
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Form>
|
||||
</Modal.Body>
|
||||
</Modal>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
export default AccessTypeManagement;
|
||||
158
src/components/Dashboard/CSS/CSS/CommonStyle.css
Normal file
158
src/components/Dashboard/CSS/CSS/CommonStyle.css
Normal file
@@ -0,0 +1,158 @@
|
||||
.table-responsive{
|
||||
box-shadow: 2px 2px 6px -1px grey;
|
||||
border-radius: 0.8rem;
|
||||
|
||||
}
|
||||
|
||||
.thead-light{
|
||||
font-size: 1.1rem;
|
||||
font-family: "Montserrat", sans-serif;
|
||||
font-optical-sizing: auto;
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
|
||||
}
|
||||
|
||||
.tbody{
|
||||
font-size: 1.0rem;
|
||||
font-family: "Montserrat", sans-serif;
|
||||
font-optical-sizing: auto;
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.sub-menu-header, .sub-menu-column {
|
||||
min-width: 150px; /* Adjust the width as necessary */
|
||||
}
|
||||
|
||||
.sub-menu-column {
|
||||
text-align: center; /* Ensure the button is centered in the cell */
|
||||
}
|
||||
|
||||
/* Datatype - 1 */
|
||||
.form_body{
|
||||
font-size: 1.1rem;
|
||||
font-family: "Montserrat", sans-serif;
|
||||
font-optical-sizing: auto;
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* datatype-4 styles */
|
||||
.payment_details{
|
||||
font-size: 1.7rem;
|
||||
font-family: "Montserrat", sans-serif;
|
||||
font-optical-sizing: auto;
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* datatype-5 & datatype-6 styles,system parameter*/
|
||||
.title_main{
|
||||
font-size: 1.7rem;
|
||||
font-family: "Montserrat", sans-serif;
|
||||
font-optical-sizing: auto;
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
color: #0E6591;
|
||||
}
|
||||
|
||||
.form_content{
|
||||
font-size: 1.1rem;
|
||||
font-family: "Montserrat", sans-serif;
|
||||
font-optical-sizing: auto;
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
|
||||
.heading_main{
|
||||
font-size: 1.1rem;
|
||||
font-family: "Montserrat", sans-serif;
|
||||
font-optical-sizing: auto;
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
|
||||
}
|
||||
|
||||
/* userdetails views */
|
||||
.user-details-wrapper {
|
||||
padding-top: 11rem;
|
||||
padding-bottom: 5rem;/* Adjust as needed for the space between the navbar and component */
|
||||
margin-top: -10rem;
|
||||
}
|
||||
|
||||
|
||||
/* user report */
|
||||
table thead.custom_header th {
|
||||
font-size: 0.8rem;
|
||||
font-family: "Montserrat", sans-serif;
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
color: #333; /* Adjust text color if needed */
|
||||
/* Light background color for header */
|
||||
}
|
||||
|
||||
.custom_button {
|
||||
background-color: #0E6591 !important;
|
||||
border-color: #0E6591 !important;
|
||||
color: azure;
|
||||
}
|
||||
|
||||
.custom_button:hover {
|
||||
background-color: #0B4C6A !important; /* Optional: darker shade on hover */
|
||||
border-color: #0B4C6A !important;
|
||||
}
|
||||
|
||||
.custom-hover-border:focus {
|
||||
border-color: #0E6591 !important;
|
||||
box-shadow: 0 0 5px rgba(14, 101, 145, 0.5);
|
||||
}
|
||||
|
||||
.custom-checkbox:checked {
|
||||
border-color: #0E6591;
|
||||
background-color: #0E6591;
|
||||
}
|
||||
|
||||
.custom_manage_column_button{
|
||||
color: #0E6591 !important;
|
||||
border-color: #0E6591 !important;
|
||||
}
|
||||
|
||||
.custom_manage_column_button:hover{
|
||||
background-color: #0E6591 !important;
|
||||
color: #fff !important; /* Changes text to white on hover */
|
||||
border-color: #0E6591 !important;
|
||||
}
|
||||
|
||||
.title_name{
|
||||
color: #0E6591;
|
||||
font-size: 1.1rem;
|
||||
font-family: "Montserrat", sans-serif;
|
||||
font-optical-sizing: auto;
|
||||
font-weight: 900;
|
||||
font-style: bold;
|
||||
}
|
||||
|
||||
|
||||
/* system parameter */
|
||||
.system_parameter{
|
||||
border-radius: 2px;
|
||||
box-shadow: 2px 2px 2px grey;
|
||||
|
||||
}
|
||||
|
||||
.table-wrapper {
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch; /* Smooth scrolling for touch devices */
|
||||
padding: 0.5rem; /* Optional padding for better spacing */
|
||||
}
|
||||
|
||||
.table-flush {
|
||||
width: 100%;
|
||||
min-width: 900px; /* Adjust based on your table's expected width */
|
||||
}
|
||||
|
||||
.table-light.custom_header {
|
||||
white-space: nowrap; /* Prevent headers from breaking into multiple lines */
|
||||
}
|
||||
513
src/components/Dashboard/CSS/CSS/Dashboard.css
Normal file
513
src/components/Dashboard/CSS/CSS/Dashboard.css
Normal file
@@ -0,0 +1,513 @@
|
||||
* {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
box-sizing: border-box;
|
||||
/* font-family: "Poppins", sans-serif; */
|
||||
font-family: "Montserrat", sans-serif;
|
||||
}
|
||||
|
||||
.main-side{
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
|
||||
}
|
||||
|
||||
.topbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right:0;
|
||||
width: 100%;
|
||||
height: 70px;
|
||||
background-color: #0B4C6A;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
|
||||
.navbar-icons {
|
||||
/* display: flex;
|
||||
align-items: center;
|
||||
gap: 36px;
|
||||
margin-left: 10%;
|
||||
color: rgb(245, 240, 240);
|
||||
height: 80px;
|
||||
width: 80px; */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 36px;
|
||||
margin: 0 auto; /* Center the navbar icons */
|
||||
color: rgb(245, 240, 240);
|
||||
|
||||
}
|
||||
|
||||
.navbar-icons i {
|
||||
margin: 0 15px;
|
||||
cursor: pointer;
|
||||
transition: color 0.3s;
|
||||
|
||||
}
|
||||
|
||||
.navbar-icons i:hover {
|
||||
color:black; /* Change to your desired hover color */
|
||||
}
|
||||
|
||||
.navbar-avatar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: auto;
|
||||
/* Push the avatar to the right */
|
||||
}
|
||||
|
||||
.navbar-avatar i {
|
||||
width: 60px; /* Adjust the size as needed */
|
||||
height: 60px; /* Adjust the size as needed */
|
||||
border-radius: 50%; /* Make it circular */
|
||||
margin-left: 10px;
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
|
||||
.user-Profile-Icon{
|
||||
height: 2rem;
|
||||
width: 2rem;
|
||||
color:white;
|
||||
}
|
||||
|
||||
.toggle-button {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 24px;
|
||||
cursor: pointer;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.toggle-button:hover {
|
||||
transform: scale(1.1); /* Scale effect on hover */
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
margin-top: 4rem; /* Move the sidebar container down */
|
||||
}
|
||||
|
||||
.slider-container {
|
||||
position: relative; /* Adjust to fit your design */
|
||||
z-index: 10; /* Ensure it is above the sidebar */
|
||||
transition: transform 0.3s ease; /* Smooth transition for sliding effect */
|
||||
}
|
||||
.dropdown-menu {
|
||||
right: 0; /* Align dropdown to the right */
|
||||
}
|
||||
|
||||
.Dashboard-logout {
|
||||
display: flex;
|
||||
background-color: #ebf0ee;
|
||||
color: rgb(10, 10, 10);
|
||||
border: none;
|
||||
padding: 3px 10px 3px 10px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
border: 2px solid black;
|
||||
margin-left: auto;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.Dashboard-logout:hover {
|
||||
background-color: #4d4e4d;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.user {
|
||||
position: relative;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
justify-self: right;
|
||||
}
|
||||
|
||||
.main {
|
||||
position: absolute;
|
||||
top: 70px;
|
||||
left: 260px;
|
||||
width: calc(100% - 260px);
|
||||
min-height: calc(100vh - 70px);
|
||||
background: #fff;
|
||||
padding: 20px;
|
||||
justify-content: center;
|
||||
flex-grow: 1;
|
||||
transition: margin-left 00.3 ease-in-out;
|
||||
}
|
||||
/*
|
||||
Sidebar icon container (beneath top bar) */
|
||||
.sidebar-icon-container {
|
||||
position: absolute;
|
||||
top: 100px;
|
||||
left: 0;
|
||||
width:250px;
|
||||
transition: margin-left 0.3s ease;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* Icon color when sidebar is closed (black) */
|
||||
.menu-icon {
|
||||
color: black !important;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
float: left;
|
||||
width: 250px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
height:100vh;
|
||||
bottom: 0;
|
||||
background-color:#EDF5F4 !important;
|
||||
color: #4A4A4A !important;
|
||||
padding: 20px;
|
||||
transition: transform 0.3s ease-in-out;
|
||||
transform: translateX(-100%);
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||
z-index: 100;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.sidebar.open {
|
||||
/* Styles when sidebar is open */
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.sidebar.closed {
|
||||
/* Styles when sidebar is closed */
|
||||
transform: translateX(-100%); /* or adjust to your design */
|
||||
}
|
||||
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.sidebar ul li {
|
||||
margin-bottom: 10px;
|
||||
padding: 10px 10px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
.sidebar ul li:hover {
|
||||
background-color:#2E4852;
|
||||
color:black;
|
||||
}
|
||||
|
||||
.sidebar ul li a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
|
||||
.cards {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 20px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.card {
|
||||
flex: 1 1 200px;
|
||||
margin: 10px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 7px 25px rgba(5, 5, 5, 0.08);
|
||||
background-color: #fff;
|
||||
border-radius: 10px;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.number {
|
||||
font-size: 35px;
|
||||
font-weight: 500;
|
||||
color: #020705;
|
||||
}
|
||||
|
||||
.card-name {
|
||||
color: #0c0404;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Card styling for SetupView */
|
||||
.usercards {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
padding: 20 px;
|
||||
margin-top: 30px; /* Offset for the fixed top bar */
|
||||
}
|
||||
|
||||
.usercard {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
border: 2px solid black;
|
||||
margin-left: auto;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
padding: 20px;
|
||||
width: calc(25% - 20px); /* Responsive width */
|
||||
cursor: pointer;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.usercard:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.usercard i {
|
||||
font-size: 20px;
|
||||
color: #333;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.usercard h3 {
|
||||
margin-bottom: 5px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.usercard p {
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.imgchart {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
.imgchart img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.main {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.report-card {
|
||||
flex: 1 1 calc(33.333% - 40px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: start;
|
||||
padding: 20px;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||
border-radius: 10px;
|
||||
background-color: #ffffff;
|
||||
text-align: center;
|
||||
width: 200px;
|
||||
height: auto;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.report-card:hover {
|
||||
box-shadow: #0c0404;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.user-details {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-around;
|
||||
gap: 20px;
|
||||
padding: 20px;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.sidebar {
|
||||
float: none;
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
|
||||
.main {
|
||||
margin-left: 0;
|
||||
}
|
||||
.sidebar.open {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.main {
|
||||
margin-left: 0px;
|
||||
}
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.card {
|
||||
flex-basis: 90%;
|
||||
margin: 5px auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.card {
|
||||
flex-basis: 90%;
|
||||
margin: 5px auto;
|
||||
}
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
width: auto;
|
||||
max-width: 500px;
|
||||
height: auto;
|
||||
aspect-ratio: 2 / 1;
|
||||
margin: 20px auto;
|
||||
padding: 10px;
|
||||
background-color: #f0f0f0;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.chart-container {
|
||||
max-width: 500px;
|
||||
padding: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.chart-container {
|
||||
max-width: 50%;
|
||||
aspect-ratio: 1 / 1;
|
||||
}
|
||||
}
|
||||
@media (max-width: 972px) {
|
||||
.sidebar {
|
||||
position: fixed; /* Fixed positioning for small screens */
|
||||
left: 0;
|
||||
top: 70px;
|
||||
height: 100%;
|
||||
z-index: 1000; /* Ensure it's above other content */
|
||||
}
|
||||
|
||||
.main {
|
||||
margin-left: 0; /* Main content takes full width when sidebar is hidden */
|
||||
}
|
||||
}
|
||||
/* Responsive card adjustments */
|
||||
@media (max-width: 768px) {
|
||||
.usercard {
|
||||
align-items: center;
|
||||
margin-left: auto;
|
||||
width: 100%; /* Full width cards on smaller screens */
|
||||
}
|
||||
}
|
||||
@media (max-width: 480px) {
|
||||
.usercard {
|
||||
margin-left: auto;
|
||||
width: 100%; /* Full width cards on smaller screens */
|
||||
}
|
||||
}
|
||||
.menu-icon{
|
||||
color: aliceblue;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
flex: 0 0 200px; /* sidebar width */
|
||||
height: 100vh; /* full-height sidebar */
|
||||
}
|
||||
|
||||
.main {
|
||||
flex-grow: 1; /* main content takes remaining space */
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
flex-direction: column; /* stack vertically on smaller screens */
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
/* sidebar takes full width on small screens */
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.main {
|
||||
width: 100%; /* main content takes full width */
|
||||
}
|
||||
}
|
||||
|
||||
.submenu {
|
||||
display: block; /* Ensure submenus are visible */
|
||||
padding-left: 20px; /* Add padding for nested submenu */
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.submenu li {
|
||||
list-style-type: none; /* Remove bullets */
|
||||
padding-left: 10px; /* Add some indentation for submenu items */
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
position: fixed; /* Fixes the sidebar to the left */
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100vh; /* Takes the full height of the viewport */
|
||||
width: 250px; /* Adjust the width as needed */
|
||||
background-color: #413d3d;/* Background color */
|
||||
overflow-y: auto; /* Enables vertical scrolling */
|
||||
overflow-x: hidden; /* Prevents horizontal scrolling */
|
||||
transition: width 0.3s; /* Smooth transition when expanding/collapsing */
|
||||
padding: 20px; /* Padding for content inside */
|
||||
z-index: 100; /* Makes sure sidebar stays on top */
|
||||
}
|
||||
|
||||
.sidebar.open {
|
||||
width: 250px; /* Width when the sidebar is open */
|
||||
}
|
||||
|
||||
.sidebar ul {
|
||||
list-style: none; /* Removes default bullet points from list */
|
||||
padding: 0; /* Removes default padding */
|
||||
margin: 0; /* Removes default margin */
|
||||
}
|
||||
|
||||
.sidebar ul li {
|
||||
padding: 10px; /* Adds padding to each list item */
|
||||
cursor: pointer; /* Changes cursor on hover */
|
||||
transition: background-color 0.2s; /* Smooth background color change */
|
||||
}
|
||||
|
||||
.sidebar ul li:hover {
|
||||
background-color: #e2e2e2; /* Background color on hover */
|
||||
}
|
||||
|
||||
.sidebar .submenu ul {
|
||||
margin-top: 10px;
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
.main-side {
|
||||
margin-left: 250px; /* Adjust according to the sidebar width */
|
||||
padding: 20px; /* Padding for the main content area */
|
||||
}
|
||||
|
||||
@media (max-width: 971px) {
|
||||
.main-side {
|
||||
margin-left: 0; /* Resets margin when the sidebar is collapsed */
|
||||
}
|
||||
}
|
||||
73
src/components/Dashboard/CSS/CSS/DataType2.css
Normal file
73
src/components/Dashboard/CSS/CSS/DataType2.css
Normal file
@@ -0,0 +1,73 @@
|
||||
.container {
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
.uploaded-file {
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.scanned-result {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.barcode-scanner {
|
||||
width: 100%;
|
||||
height: 320px;
|
||||
border: 2px solid #ccc;
|
||||
border-radius: 8px;
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
|
||||
.custom-hover-border:focus {
|
||||
border-color: #0E6591 !important;
|
||||
box-shadow: 0 0 5px rgba(14, 101, 145, 0.5);
|
||||
}
|
||||
|
||||
|
||||
|
||||
.custom-file-input ~ .custom-file-label {
|
||||
background-color: white; /* Purple background */
|
||||
color: black; /* White text */
|
||||
border: none;
|
||||
padding: 0.5rem 1rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.custom-file-input:focus ~ .custom-file-label {
|
||||
outline: none;
|
||||
|
||||
}
|
||||
|
||||
|
||||
.upload-section {
|
||||
background-color: #f8f9fa; /* Light background for the card */
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.custom-file-input ~ .custom-file-label {
|
||||
background-color:white ;
|
||||
color: black; /* White text */
|
||||
border: none;
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.custom-file-input:focus ~ .custom-file-label {
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 0.2rem rgba(111, 66, 193, 0.25);
|
||||
}
|
||||
|
||||
.video-preview {
|
||||
max-height: 300px;
|
||||
border-radius: 8px;
|
||||
margin-top: 20px;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
20
src/components/Dashboard/CSS/CSS/Datatype1.css
Normal file
20
src/components/Dashboard/CSS/CSS/Datatype1.css
Normal file
@@ -0,0 +1,20 @@
|
||||
.custom-radio:checked {
|
||||
background-color: #0E6591;
|
||||
border-color: #0E6591;
|
||||
}
|
||||
|
||||
.custom-radio:checked + .form-check-label::before {
|
||||
color: #0E6591;
|
||||
border-color: #0E6591;
|
||||
}
|
||||
|
||||
|
||||
.custom-hover-border:focus {
|
||||
border-color: #0E6591 !important;
|
||||
box-shadow: 0 0 5px rgba(14, 101, 145, 0.5);
|
||||
}
|
||||
|
||||
.card{
|
||||
box-shadow: 2px 2px 2px 2px grey;
|
||||
border-radius: 2px #0e6591;
|
||||
}
|
||||
89
src/components/Dashboard/CSS/CSS/MenuMaitenance.css
Normal file
89
src/components/Dashboard/CSS/CSS/MenuMaitenance.css
Normal file
@@ -0,0 +1,89 @@
|
||||
.table-responsive{
|
||||
box-shadow: 2px 2px 6px -1px grey;
|
||||
|
||||
}
|
||||
|
||||
.thead-light{
|
||||
/* <uniquifier>: Use a unique and descriptive class name
|
||||
<weight>: Use a value from 100 to 900 */
|
||||
|
||||
|
||||
font-size: 1.1rem;
|
||||
font-family: "Montserrat", sans-serif;
|
||||
font-optical-sizing: auto;
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
|
||||
}
|
||||
|
||||
.tbody{
|
||||
font-size: 1.0rem;
|
||||
font-family: "Montserrat", sans-serif;
|
||||
font-optical-sizing: auto;
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.text-primary{
|
||||
font-family: "Montserrat", sans-serif;
|
||||
font-optical-sizing: auto;
|
||||
}
|
||||
|
||||
.title_main{
|
||||
font-size: 1.7rem;
|
||||
font-family: "Montserrat", sans-serif;
|
||||
font-optical-sizing: auto;
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
color: #0E6591;
|
||||
}
|
||||
|
||||
table thead.custom_header th {
|
||||
font-size: 0.8rem;
|
||||
font-family: "Montserrat", sans-serif;
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
color: #333; /* Adjust text color if needed */
|
||||
/* Light background color for header */
|
||||
}
|
||||
|
||||
.custom_button {
|
||||
background-color: #0E6591 !important;
|
||||
border-color: #0E6591 !important;
|
||||
color: azure;
|
||||
}
|
||||
|
||||
.custom_button:hover {
|
||||
background-color: #0B4C6A !important; /* Optional: darker shade on hover */
|
||||
border-color: #0B4C6A !important;
|
||||
}
|
||||
|
||||
.custom-hover-border:focus {
|
||||
border-color: #0E6591 !important;
|
||||
box-shadow: 0 0 5px rgba(14, 101, 145, 0.5);
|
||||
}
|
||||
|
||||
.custom-checkbox:checked {
|
||||
border-color: #0E6591;
|
||||
background-color: #0E6591;
|
||||
}
|
||||
|
||||
.custom_manage_column_button{
|
||||
color: #0E6591 !important;
|
||||
border-color: #0E6591 !important;
|
||||
}
|
||||
|
||||
.custom_manage_column_button:hover{
|
||||
background-color: #0E6591 !important;
|
||||
color: #fff !important; /* Changes text to white on hover */
|
||||
border-color: #0E6591 !important;
|
||||
}
|
||||
|
||||
.title_name{
|
||||
color: #0E6591;
|
||||
font-size: 1.1rem;
|
||||
font-family: "Montserrat", sans-serif;
|
||||
font-optical-sizing: auto;
|
||||
font-weight: 900;
|
||||
font-style: bold;
|
||||
}
|
||||
45
src/components/Dashboard/CSS/CSS/ProfileDropdown.css
Normal file
45
src/components/Dashboard/CSS/CSS/ProfileDropdown.css
Normal file
@@ -0,0 +1,45 @@
|
||||
/* ProfileDropdown.css */
|
||||
.profile-dropdown {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.profile-circle {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.profile-circle img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
position: absolute;
|
||||
top: 60px;
|
||||
right: 0;
|
||||
background-color: white;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.dropdown-menu button {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: none;
|
||||
background: none;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dropdown-menu button:hover {
|
||||
background-color: #f1f1f1;
|
||||
}
|
||||
|
||||
50
src/components/Dashboard/CSS/CSS/SetupView.css
Normal file
50
src/components/Dashboard/CSS/CSS/SetupView.css
Normal file
@@ -0,0 +1,50 @@
|
||||
.usercards{
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr); /* 4 columns */
|
||||
gap: 15px;
|
||||
margin-top:9rem;
|
||||
margin-right: 2rem;
|
||||
margin-left:2rem;
|
||||
margin-bottom:2rem;
|
||||
}
|
||||
|
||||
.usercard{
|
||||
background-color: #f8f9fa;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||
}
|
||||
|
||||
.usercard i {
|
||||
color:#0E6591;
|
||||
}
|
||||
|
||||
.usercard:hover{
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.usercard h3{
|
||||
color: #0E6591; /* Change the card title color to red */
|
||||
font-family: 'GoogleFontName', sans-serif;
|
||||
font-size: large; /* Apply the Google font */
|
||||
}
|
||||
|
||||
@media (max-width:1200px) {
|
||||
.usercards{
|
||||
grid-template-columns: repeat(3,1fr);
|
||||
}
|
||||
}
|
||||
@media (max-width:768px) {
|
||||
.usercards{
|
||||
grid-template-columns: repeat(2,1fr);
|
||||
}
|
||||
}
|
||||
@media (max-width:576px) {
|
||||
.usercards{
|
||||
grid-template-columns: repeat(1fr);
|
||||
}
|
||||
}
|
||||
106
src/components/Dashboard/CSS/CSS/UserMaintainanceView.css
Normal file
106
src/components/Dashboard/CSS/CSS/UserMaintainanceView.css
Normal file
@@ -0,0 +1,106 @@
|
||||
.table-responsive{
|
||||
box-shadow: 2px 2px 6px -1px grey;
|
||||
|
||||
}
|
||||
|
||||
.thead-light{
|
||||
/* <uniquifier>: Use a unique and descriptive class name
|
||||
<weight>: Use a value from 100 to 900 */
|
||||
|
||||
|
||||
font-size: 1.1rem;
|
||||
font-family: "Montserrat", sans-serif;
|
||||
font-optical-sizing: auto;
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
|
||||
}
|
||||
|
||||
.tbody{
|
||||
font-size: 1.0rem;
|
||||
font-family: "Montserrat", sans-serif;
|
||||
font-optical-sizing: auto;
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.sub-menu-header, .sub-menu-column {
|
||||
min-width: 150px; /* Adjust the width as necessary */
|
||||
}
|
||||
|
||||
.sub-menu-column {
|
||||
text-align: center; /* Ensure the button is centered in the cell */
|
||||
}
|
||||
|
||||
.title_main{
|
||||
font-size: 1.7rem;
|
||||
font-family: "Montserrat", sans-serif;
|
||||
font-optical-sizing: auto;
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
color: #0E6591;
|
||||
}
|
||||
|
||||
.custom_header{
|
||||
font-size: 0.8rem;
|
||||
font-family: "Montserrat", sans-serif;
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.custom-checkbox:checked{
|
||||
border-color: #0E6591;
|
||||
background-color: #0E6591;
|
||||
}
|
||||
|
||||
|
||||
.status{
|
||||
font-weight: bold;
|
||||
text-align: center; /* Ensures the text is centered */
|
||||
display: inline-block; /* Keeps the background confined to the text */
|
||||
padding: 4px 8px; /* Adds padding for background */
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* Custom checkbox styles */
|
||||
.custom-checkbox:checked .form-check-input:checked {
|
||||
background-color: #0E6591; /* Your custom color */
|
||||
border-color: #0E6591; /* Custom border color */
|
||||
}
|
||||
|
||||
.custom-checkbox .form-check-input:focus {
|
||||
border-color: #0E6591; /* Focus state color */
|
||||
box-shadow: 0 0 0 0.25rem rgba(14, 101, 145, 0.5); /* Focus glow effect */
|
||||
}
|
||||
|
||||
.custom-checkbox .form-check-input {
|
||||
border-radius: 0.25rem; /* Optional: To make checkbox corners rounder */
|
||||
}
|
||||
|
||||
|
||||
.custom_button {
|
||||
background-color: #0E6591 !important;
|
||||
border-color: #0E6591 !important;
|
||||
color: azure;
|
||||
}
|
||||
|
||||
.custom_button:hover {
|
||||
background-color: #0B4C6A !important; /* Optional: darker shade on hover */
|
||||
border-color: #0B4C6A !important;
|
||||
}
|
||||
|
||||
.custom-hover-border:focus {
|
||||
border-color: #0E6591 !important;
|
||||
box-shadow: 0 0 5px rgba(14, 101, 145, 0.5);
|
||||
}
|
||||
|
||||
.custom-hover-border:focus {
|
||||
border-color: #0b6592 !important;
|
||||
box-shadow: 0 0 5px rgba(14, 101, 145, 0.5);
|
||||
}
|
||||
|
||||
.pagination{
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
236
src/components/Dashboard/Codeextension.js
Normal file
236
src/components/Dashboard/Codeextension.js
Normal file
@@ -0,0 +1,236 @@
|
||||
// import React, { useState, useEffect, useRef } from 'react';
|
||||
// import { Box, Button, Modal, TextField, Typography, FormControl, FormControlLabel, Checkbox, Radio, RadioGroup, Autocomplete } from '@mui/material';
|
||||
// import { DataGrid, GridToolbarContainer } from '@mui/x-data-grid';
|
||||
// import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
// import { faEllipsisV } from '@fortawesome/free-solid-svg-icons';
|
||||
// import AirplanemodeActiveIcon from '@mui/icons-material/AirplanemodeActive';
|
||||
// import { Link } from 'react-router-dom';
|
||||
// import Extension from './Extension';
|
||||
|
||||
// function CustomToolbar({ handleModal }) {
|
||||
// return (
|
||||
// <GridToolbarContainer>
|
||||
// <Button onClick={handleModal}>+</Button>
|
||||
// </GridToolbarContainer>
|
||||
// );
|
||||
// }
|
||||
|
||||
// function CodeExtension() {
|
||||
// const [menuItems, setMenuItems] = useState([]);
|
||||
// const [selectedMenuItem, setSelectedMenuItem] = useState(null);
|
||||
// const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
// const [formData, setFormData] = useState({
|
||||
// name: '',
|
||||
// email: '',
|
||||
// testing: '',
|
||||
// dataType: ''
|
||||
// });
|
||||
|
||||
// useEffect(() => {
|
||||
// const fetchData = async () => {
|
||||
// try {
|
||||
// const response = await fetch('http://localhost:9292/api/extension');
|
||||
// const data = await response.json();
|
||||
// setMenuItems(data);
|
||||
// } catch (error) {
|
||||
// console.error('Error fetching data:', error);
|
||||
// }
|
||||
// };
|
||||
|
||||
// fetchData();
|
||||
// }, []);
|
||||
|
||||
// const handleThreeDotsClick = (menuItemId) => {
|
||||
// setSelectedMenuItem(menuItemId === selectedMenuItem ? null : menuItemId);
|
||||
// };
|
||||
|
||||
// const handleModalOpen = () => {
|
||||
// setIsModalOpen(true);
|
||||
// };
|
||||
|
||||
// const handleModalClose = () => {
|
||||
// setIsModalOpen(false);
|
||||
// // Reset form data after closing modal
|
||||
// setFormData({
|
||||
// name: '',
|
||||
// email: '',
|
||||
// testing: '',
|
||||
// dataType: ''
|
||||
// });
|
||||
// };
|
||||
|
||||
// const handleChange = (e) => {
|
||||
// const { name, value } = e.target;
|
||||
// setFormData({ ...formData, [name]: value });
|
||||
// };
|
||||
|
||||
// const handleFormSubmit = (submittedDataType) => {
|
||||
// setFormData({ ...formData, dataType: submittedDataType });
|
||||
// handleModalOpen();
|
||||
// };
|
||||
|
||||
// const columns = [
|
||||
// { field: 'goto', headerName: 'Goto', width: 200, headerClassName: 'custom-header', cellClassName: 'custom-cell' },
|
||||
// { field: 'field_name', headerName: 'Field Name', width: 250, headerClassName: 'custom-header', cellClassName: 'custom-cell' },
|
||||
// { field: 'mapping', headerName: 'Mapping', width: 200, headerClassName: 'custom-header', cellClassName: 'custom-cell' },
|
||||
// { field: 'data_type', headerName: 'Data Type', width: 200, headerClassName: 'custom-header', cellClassName: 'custom-cell' },
|
||||
// {
|
||||
// field: 'actions',
|
||||
// headerName: 'Actions',
|
||||
// width: 150,
|
||||
// renderCell: ({ row }) => (
|
||||
// <div>
|
||||
// <div className="three-dots" onClick={() => handleThreeDotsClick(row.id)}>
|
||||
// <FontAwesomeIcon icon={faEllipsisV} />
|
||||
// </div>
|
||||
// {selectedMenuItem === row.id && (
|
||||
// <div className="popover">
|
||||
// {/* Implement your actions buttons here */}
|
||||
// </div>
|
||||
// )}
|
||||
// </div>
|
||||
// ),
|
||||
// },
|
||||
// ];
|
||||
|
||||
// const renderInputField = () => {
|
||||
// switch (formData.dataType) {
|
||||
// case 'date':
|
||||
// return (
|
||||
// <TextField
|
||||
// label="Date"
|
||||
// name="date"
|
||||
// type="date"
|
||||
// value={formData.date}
|
||||
// onChange={handleChange}
|
||||
// fullWidth
|
||||
// />
|
||||
// );
|
||||
// case 'textfield':
|
||||
// return (
|
||||
// <TextField
|
||||
// label="Text Field"
|
||||
// name="textfield"
|
||||
// value={formData.textfield}
|
||||
// onChange={handleChange}
|
||||
// fullWidth
|
||||
// />
|
||||
// );
|
||||
// case 'longtext':
|
||||
// return (
|
||||
// <TextField
|
||||
// label="Long Text"
|
||||
// name="longtext"
|
||||
// value={formData.longtext}
|
||||
// onChange={handleChange}
|
||||
// multiline
|
||||
// rows={4}
|
||||
// fullWidth
|
||||
// />
|
||||
// );
|
||||
// case 'checkbox':
|
||||
// return (
|
||||
// <FormControlLabel
|
||||
// control={
|
||||
// <Checkbox
|
||||
// checked={formData.checkbox || false}
|
||||
// onChange={(e) => setFormData({ ...formData, checkbox: e.target.checked })}
|
||||
// />
|
||||
// }
|
||||
// label="Checkbox"
|
||||
// />
|
||||
// );
|
||||
// case 'radiobutton':
|
||||
// return (
|
||||
// <FormControl component="fieldset">
|
||||
// <RadioGroup
|
||||
// name="radiobutton"
|
||||
// value={formData.radiobutton || ''}
|
||||
// onChange={(e) => setFormData({ ...formData, radiobutton: e.target.value })}
|
||||
// >
|
||||
// <FormControlLabel value="option1" control={<Radio />} label="Option 1" />
|
||||
// <FormControlLabel value="option2" control={<Radio />} label="Option 2" />
|
||||
// </RadioGroup>
|
||||
// </FormControl>
|
||||
// );
|
||||
// case 'autocomplete':
|
||||
// return (
|
||||
// <Autocomplete
|
||||
// options={['Option 1', 'Option 2', 'Option 3']}
|
||||
// renderInput={(params) => <TextField {...params} label="Autocomplete" />}
|
||||
// value={formData.autocomplete || ''}
|
||||
// onChange={(e, newValue) => setFormData({ ...formData, autocomplete: newValue })}
|
||||
// fullWidth
|
||||
// />
|
||||
// );
|
||||
// default:
|
||||
// return null;
|
||||
// }
|
||||
// };
|
||||
|
||||
// return (
|
||||
// <>
|
||||
// <Box sx={{ position: 'fixed', top: 0, left: 0, width: '100%', zIndex: 1 }}>
|
||||
// {/* Your header content here */}
|
||||
// </Box>
|
||||
// <Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100vh' }}>
|
||||
// <Box sx={{ width: '80%', maxWidth: 1200, marginTop: '100px' }}>
|
||||
// <DataGrid
|
||||
// rows={menuItems}
|
||||
// columns={columns}
|
||||
// pageSize={10}
|
||||
// components={{
|
||||
// Toolbar: () => (
|
||||
// <CustomToolbar
|
||||
// handleModal={handleModalOpen}
|
||||
// />
|
||||
// ),
|
||||
// }}
|
||||
// />
|
||||
// <Modal open={isModalOpen} onClose={handleModalClose} centered>
|
||||
// <Box sx={{ width: 400, bgcolor: 'background.paper', p: 4, position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%, -50%)' }}>
|
||||
// <Extension onSubmit={handleFormSubmit} />
|
||||
// <Typography variant="h5" gutterBottom sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
// <Link to="/Extension"><AirplanemodeActiveIcon sx={{ mr: 1 }} /></Link> Add Item
|
||||
// </Typography>
|
||||
// <form onSubmit={(e) => { e.preventDefault(); handleFormSubmit(formData.dataType); }}>
|
||||
// <div>
|
||||
// <TextField
|
||||
// label="Name"
|
||||
// name="name"
|
||||
// value={formData.name}
|
||||
// onChange={handleChange}
|
||||
// fullWidth
|
||||
// />
|
||||
// </div>
|
||||
// <div>
|
||||
// <TextField
|
||||
// label="Email"
|
||||
// name="email"
|
||||
// value={formData.email}
|
||||
// onChange={handleChange}
|
||||
// fullWidth
|
||||
// />
|
||||
// </div>
|
||||
// <div>
|
||||
// <TextField
|
||||
// label="Testing"
|
||||
// name="testing"
|
||||
// value={formData.testing}
|
||||
// onChange={handleChange}
|
||||
// fullWidth
|
||||
// />
|
||||
// </div>
|
||||
// {renderInputField()}
|
||||
// <Button type="submit" variant="contained" sx={{ mt: 2, mr: 1 }}>Submit</Button>
|
||||
// <Button variant="outlined" onClick={handleModalClose} sx={{ mt: 2 }}>Cancel</Button>
|
||||
// </form>
|
||||
// </Box>
|
||||
// </Modal>
|
||||
// </Box>
|
||||
// </Box>
|
||||
// </>
|
||||
// );
|
||||
// }
|
||||
|
||||
// export default CodeExtension;
|
||||
630
src/components/Dashboard/Dashboard.js
Normal file
630
src/components/Dashboard/Dashboard.js
Normal file
@@ -0,0 +1,630 @@
|
||||
// import React, { useState, useEffect } from "react";
|
||||
// import { useNavigate, Link } from "react-router-dom";
|
||||
// import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
// import {
|
||||
// faHouse,
|
||||
// faGear,
|
||||
// faUsers,
|
||||
// faBars,
|
||||
// faUserCircle,
|
||||
// faSignOutAlt
|
||||
// } from "@fortawesome/free-solid-svg-icons";
|
||||
// import "./Dashboard.css";
|
||||
// import { Bar } from "react-chartjs-2";
|
||||
// import Chart from "chart.js/auto";
|
||||
// import SetupView from "./SetupView";
|
||||
// import DashboardView from "./DashboardView";
|
||||
// import UserDetailsView from "./UserDetailsView";
|
||||
// import UserMaintenanceView from "./UserMaintenanceView";
|
||||
// import AccessType from "./AccessType";
|
||||
// import SystemParameter from "./SystemParameter";
|
||||
// import MenuAccessControl from "./MenuAccessControl";
|
||||
// import MenuMaintenance from "./MenuMaintenance";
|
||||
// import UserGroupMaintenance from "./UserGroupMaintenance";
|
||||
// import APIRegistry from "./APIRegistry";
|
||||
// import TOKENRegistry from "./TOKENRegistry";
|
||||
// import DataType1 from "./DataType1";
|
||||
// import DataType2 from "./DataType2";
|
||||
// import DataType3 from "./DataType3";
|
||||
// import DataType4 from "./DataType4";
|
||||
// import DataType5 from "./DataType5";
|
||||
// import DataType6 from "./DataType6";
|
||||
// import DynamicTable from "./DynamicTable";
|
||||
// import CodeExtension from "./Codeextension";
|
||||
// // import DashboardCharts from "./DashboardCharts";
|
||||
// import Dropdown from "react-bootstrap/Dropdown";
|
||||
// import { removeToken } from "../../utils/tokenService";
|
||||
// import AccordionPage from "./UIComponents/Accordion";
|
||||
// import BadgesPage from "./UIComponents/BadgesPage";
|
||||
// import DashViewone from "./UIComponents/DashViewone";
|
||||
|
||||
// const HomeView = ({ barChartData, barChartOptions }) => (
|
||||
// <div>
|
||||
// <div className="cards">
|
||||
// <div className="card">
|
||||
// <div className="card-content">
|
||||
// <div className="number">1</div>
|
||||
// <div className="card-name">Index</div>
|
||||
// </div>
|
||||
// </div>
|
||||
// <div className="card">
|
||||
// <div className="card-content">
|
||||
// <div className="number">2</div>
|
||||
// <div className="card-name">Index</div>
|
||||
// </div>
|
||||
// </div>
|
||||
// <div className="card">
|
||||
// <div className="card-content">
|
||||
// <div className="number">3</div>
|
||||
// <div className="card-name">Index</div>
|
||||
// </div>
|
||||
// </div>
|
||||
// </div>
|
||||
// <div
|
||||
// className="chart-container"
|
||||
// style={{
|
||||
// width: "100%",
|
||||
// maxWidth: "700px",
|
||||
// height: "400px",
|
||||
// display: "flex",
|
||||
// justifyContent: "center",
|
||||
// marginTop: "20px",
|
||||
// }}
|
||||
// >
|
||||
// <Bar data={barChartData} options={barChartOptions} />
|
||||
// </div>
|
||||
// </div>
|
||||
// );
|
||||
// function Dashboard() {
|
||||
// const [currentView, setCurrentView] = useState("home");
|
||||
// const [isSidebarOpen, setIsSidebarOpen] = useState(false);
|
||||
// const [menuItems, setMenuItems] = useState([]);
|
||||
// const [error, setError] = useState("");
|
||||
// const navigate = useNavigate();
|
||||
|
||||
// useEffect(() => {
|
||||
// const handleResize = () => {
|
||||
// setIsSidebarOpen(window.innerWidth >= 972);
|
||||
// };
|
||||
// handleResize();
|
||||
// window.addEventListener("resize", handleResize);
|
||||
// return () => window.removeEventListener("resize", handleResize);
|
||||
// }, []);
|
||||
|
||||
// useEffect(() => {
|
||||
// const fetchMenuItems = async () => {
|
||||
// const apiUrl = `${process.env.REACT_APP_API_URL}/fndMenu/menuloadbyuser`;
|
||||
// const token = localStorage.getItem("authToken");
|
||||
|
||||
// if (!token) {
|
||||
// console.error("Authorization token is missing.");
|
||||
// setError("Authorization token is missing.");
|
||||
// return;
|
||||
// }
|
||||
|
||||
// try {
|
||||
// const response = await fetch(apiUrl, {
|
||||
// method: "GET",
|
||||
// headers: {
|
||||
// "Content-Type": "application/json",
|
||||
// Authorization: `Bearer ${token}`,
|
||||
// },
|
||||
// });
|
||||
|
||||
// if (!response.ok) {
|
||||
// throw new Error(`Error: ${response.status}`);
|
||||
// }
|
||||
|
||||
// const data = await response.json();
|
||||
// setMenuItems(data);
|
||||
// } catch (error) {
|
||||
// console.error("Fetching error:", error);
|
||||
// setError(error.toString());
|
||||
// }
|
||||
// };
|
||||
|
||||
// fetchMenuItems();
|
||||
// }, []);
|
||||
|
||||
// const barChartData = {
|
||||
// labels: ["Label1", "Label2", "Label3"],
|
||||
// datasets: [
|
||||
// {
|
||||
// label: "Dataset 1",
|
||||
// data: [65, 59, 80],
|
||||
// backgroundColor: [
|
||||
// "rgba(255, 99, 132, 0.2)",
|
||||
// "rgba(54, 162, 235, 0.2)",
|
||||
// "rgba(255, 206, 86, 0.2)",
|
||||
// ],
|
||||
// borderColor: [
|
||||
// "rgba(255, 99, 132, 1)",
|
||||
// "rgba(54, 162, 235, 1)",
|
||||
// "rgba(255, 206, 86, 1)",
|
||||
// ],
|
||||
// borderWidth: 1,
|
||||
// },
|
||||
// ],
|
||||
// };
|
||||
|
||||
// const barChartOptions = {
|
||||
// scales: {
|
||||
// y: {
|
||||
// beginAtZero: true,
|
||||
// },
|
||||
// },
|
||||
// maintainAspectRatio: false,
|
||||
// };
|
||||
|
||||
// if (error) {
|
||||
// return <p>Error fetching data: {error}</p>;
|
||||
// }
|
||||
// const handleLogout = () => {
|
||||
// removeToken();
|
||||
// navigate("/#", { replace: true }); // Redirect to login page
|
||||
// };
|
||||
|
||||
// const toggleSidebar = () => {
|
||||
// setIsSidebarOpen(!isSidebarOpen);
|
||||
// };
|
||||
|
||||
// const renderView = () => {
|
||||
// // console.log("renderning")
|
||||
// console.log("Current View:", currentView);
|
||||
// switch (currentView) {
|
||||
// // case "home":
|
||||
// // return (
|
||||
// // <HomeView
|
||||
// // barChartData={barChartData}
|
||||
// // barChartOptions={barChartOptions}
|
||||
// // />
|
||||
// // );
|
||||
// case "home": {
|
||||
// console.log("render it")
|
||||
// return (
|
||||
|
||||
// <>
|
||||
// <AccordionPage />
|
||||
// <BadgesPage />
|
||||
// <DashViewone/>
|
||||
// </>
|
||||
// );
|
||||
// }
|
||||
|
||||
// case "setup":
|
||||
// console.log("reder on");
|
||||
// return (
|
||||
// <SetupView
|
||||
// onUserMaintenance={() => setCurrentView("userMaintenance")}
|
||||
// onMenuAccessControl={() => setCurrentView("menuAccessControl")}
|
||||
// onUserGroupMaintenance={() =>
|
||||
// setCurrentView("userGroupMaintenance")
|
||||
// }
|
||||
// onSystemParameter={() => setCurrentView("systemParameter")}
|
||||
// onMenuMaintenance={() => setCurrentView("menuMaintenance")}
|
||||
// onAccessType={() => setCurrentView("accessType")}
|
||||
// onAPIregistry={() => setCurrentView("apiRegistry")}
|
||||
// onTokenregistry={() => setCurrentView("tokenRegistry")}
|
||||
// onDataType1={() => setCurrentView("dataType1")}
|
||||
// onDataType2={() => setCurrentView("dataType2")}
|
||||
// onDataType3={() => setCurrentView("dataType3")}
|
||||
// onDataType4={() => setCurrentView("dataType4")}
|
||||
// onDataType5={() => setCurrentView("dataType5")}
|
||||
// onDataType6={() => setCurrentView("dataType6")}
|
||||
// onDynamicTable={() => setCurrentView("dynamicTable")}
|
||||
// oncodeExtension={() => setCurrentView("codeExtension")}
|
||||
// />
|
||||
// );
|
||||
// // case "dashboard":
|
||||
// // return <DashboardView
|
||||
// // onDashboardCharts={()=> setCurrentView("dashboardcharts")}
|
||||
// // />;
|
||||
// case "userdetails":
|
||||
// console.log("render come")
|
||||
// return <UserDetailsView />;
|
||||
|
||||
// case "userMaintenance":
|
||||
// return <UserMaintenanceView />;
|
||||
// case "accessType":
|
||||
// return <AccessType />;
|
||||
// case "systemParameter":
|
||||
// return <SystemParameter />;
|
||||
// case "menuAccessControl":
|
||||
// return <MenuAccessControl />;
|
||||
// case "menuMaintenance":
|
||||
// return <MenuMaintenance />;
|
||||
// case "userGroupMaintenance":
|
||||
// return <UserGroupMaintenance />;
|
||||
// case "apiRegistry":
|
||||
// return <APIRegistry />;
|
||||
// case "tokenRegistry":
|
||||
// return <TOKENRegistry />;
|
||||
// case "dataType1":
|
||||
// return <DataType1 />;
|
||||
// case "dataType2":
|
||||
// return <DataType2 />;
|
||||
// case "dataType3":
|
||||
// return <DataType3 />;
|
||||
// case "dataType4":
|
||||
// return <DataType4 />;
|
||||
// case "dataType5":
|
||||
// return <DataType5 />;
|
||||
// case "dataType6":
|
||||
// return <DataType6 />;
|
||||
// case "dashboardcharts":
|
||||
// return <DashboardCharts />;
|
||||
// case "dynamictable":
|
||||
// return <DynamicTable />;
|
||||
// case "codeextension":
|
||||
// return <codeExtension />;
|
||||
// default:
|
||||
// return <div>Select an option from the menu</div>;
|
||||
// }
|
||||
// };
|
||||
|
||||
// return (
|
||||
// <div className="container">
|
||||
// {/* navbar */}
|
||||
// <div className="topbar">
|
||||
// <div className="navbar-icons">
|
||||
// <i
|
||||
// onClick={() => setCurrentView("home")}
|
||||
// className="fa-solid fa-house"
|
||||
// ></i>
|
||||
// <i
|
||||
// onClick={() => setCurrentView("dashboard")} // Ensure you have a case for "dashboard" in renderView
|
||||
// className="fas fa-chart-bar"
|
||||
// ></i>
|
||||
// <i
|
||||
// onClick={() => setCurrentView("setup")}
|
||||
// className="fa-solid fa-gear"
|
||||
// ></i>
|
||||
// <i
|
||||
// onClick={() => setCurrentView("userdetails")}
|
||||
// className="fa-solid fa-users"
|
||||
// ></i>
|
||||
// </div>
|
||||
|
||||
// {/* user-profile-avatar */}
|
||||
// <div>
|
||||
// <Dropdown className="navbar-avatar">
|
||||
// <Dropdown.Toggle variant="link" id="avatar-dropdown" bsPrefix="custom-toggle" >
|
||||
// {/* <img src="/path/to/your/avatar.png" alt="User Avatar" /> */}
|
||||
// <FontAwesomeIcon icon={faUserCircle} className="user-Profile-Icon text-white" />
|
||||
// </Dropdown.Toggle>
|
||||
|
||||
// <Dropdown.Menu>
|
||||
// <Dropdown.Item onClick={() => setCurrentView("profile")}>
|
||||
// <FontAwesomeIcon icon={faUserCircle} /> Profile
|
||||
// </Dropdown.Item>
|
||||
// <Dropdown.Item onClick={handleLogout}>
|
||||
// <FontAwesomeIcon icon={faSignOutAlt} /> Logout
|
||||
// </Dropdown.Item>
|
||||
// </Dropdown.Menu>
|
||||
// </Dropdown>
|
||||
// </div>
|
||||
// </div>
|
||||
|
||||
// {/* Sidebar Icon (below the topbar) */}
|
||||
// <div className="sidebar-icon-container">
|
||||
// <FontAwesomeIcon
|
||||
// icon={faBars}
|
||||
// className="menu-icon"
|
||||
// onClick={toggleSidebar}
|
||||
// />
|
||||
// </div>
|
||||
|
||||
// {/* main view */}
|
||||
// <div className="main-side ">
|
||||
|
||||
// {/* side bar */}
|
||||
// <div className={`sidebar mt-3 ${isSidebarOpen ? "open" : ""}`}>
|
||||
// {
|
||||
// <ul>
|
||||
// {menuItems.map((item, index) => (
|
||||
// <li
|
||||
// key={index}
|
||||
// onClick={() => setCurrentView(item.menuItemId.menuItemDesc)}
|
||||
// >
|
||||
// {item.menuItemId.menuItemDesc}
|
||||
// </li>
|
||||
// ))}
|
||||
// </ul>
|
||||
// }
|
||||
|
||||
// {
|
||||
// <div>
|
||||
// {menuItems.map((menu, index) => (
|
||||
// <div key={index}>
|
||||
// <h6>{menu.menuItemDesc}</h6>
|
||||
// <ul>
|
||||
// {menu.subMenus.map((subMenu, subIndex) => (
|
||||
// <Link
|
||||
// to={`/${subMenu.menuItemDesc}`}
|
||||
// style={{ textDecoration: "none", color: "#4A4A4A" }}
|
||||
// >
|
||||
// <li key={subIndex}>{subMenu.menuItemDesc}</li>
|
||||
// </Link>
|
||||
// ))}
|
||||
// </ul>
|
||||
// </div>
|
||||
// ))}
|
||||
// </div>
|
||||
// }
|
||||
// </div>
|
||||
|
||||
// <div className="main">{renderView()}</div>
|
||||
// </div>
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
|
||||
// export default Dashboard;
|
||||
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useNavigate, Link } from "react-router-dom";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import {
|
||||
faHouse,
|
||||
faGear,
|
||||
faUsers,
|
||||
faBars,
|
||||
faUserCircle,
|
||||
faSignOutAlt,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import "./Dashboard.css";
|
||||
import { Bar } from "react-chartjs-2";
|
||||
import Chart from "chart.js/auto";
|
||||
import SetupView from "./SetupView";
|
||||
import DashboardView from "./DashboardView";
|
||||
import UserDetailsView from "./UserDetailsView";
|
||||
import UserMaintenanceView from "./UserMaintenanceView";
|
||||
import AccessType from "./AccessType";
|
||||
import SystemParameter from "./SystemParameter";
|
||||
import MenuAccessControl from "./MenuAccessControl";
|
||||
import MenuMaintenance from "./MenuMaintenance";
|
||||
import UserGroupMaintenance from "./UserGroupMaintenance";
|
||||
import APIRegistry from "./APIRegistry";
|
||||
import TOKENRegistry from "./TOKENRegistry";
|
||||
import DataType1 from "./DataType1";
|
||||
import DataType2 from "./DataType2";
|
||||
import DataType3 from "./DataType3";
|
||||
import DataType4 from "./DataType4";
|
||||
import DataType5 from "./DataType5";
|
||||
import DataType6 from "./DataType6";
|
||||
import DynamicTable from "./DynamicForm/DynamicForm2";
|
||||
import CodeExtension from "./Codeextension";
|
||||
import Dropdown from "react-bootstrap/Dropdown";
|
||||
import { removeToken } from "../../utils/tokenService";
|
||||
import AccordionPage from "./UIComponents/Accordion";
|
||||
import BadgesPage from "./UIComponents/BadgesPage";
|
||||
import DashViewone from "./UIComponents/DashViewone";
|
||||
import Sidebar from "../Dashboard/UIComponents/Sidebar";
|
||||
import Slider from "./UIComponents/Slider";
|
||||
|
||||
function Dashboard() {
|
||||
const [currentView, setCurrentView] = useState("home"); // Initial view
|
||||
const [isSidebarOpen, setIsSidebarOpen] = useState(true);
|
||||
const [menuItems, setMenuItems] = useState([]);
|
||||
const [error, setError] = useState("");
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
const handleResize = () => {
|
||||
setIsSidebarOpen(window.innerWidth >= 972);
|
||||
};
|
||||
handleResize();
|
||||
window.addEventListener("resize", handleResize);
|
||||
return () => window.removeEventListener("resize", handleResize);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchMenuItems = async () => {
|
||||
const apiUrl = `${process.env.REACT_APP_API_URL}/fndMenu/menuloadbyuser`;
|
||||
const token = localStorage.getItem("authToken");
|
||||
|
||||
if (!token) {
|
||||
console.error("Authorization token is missing.");
|
||||
setError("Authorization token is missing.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(apiUrl, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Error: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
setMenuItems(data);
|
||||
} catch (error) {
|
||||
console.error("Fetching error:", error);
|
||||
setError(error.toString());
|
||||
}
|
||||
};
|
||||
|
||||
fetchMenuItems();
|
||||
}, []);
|
||||
|
||||
const handleLogout = () => {
|
||||
removeToken();
|
||||
navigate("/#", { replace: true }); // Redirect to login page
|
||||
};
|
||||
|
||||
const toggleSlider = () => {
|
||||
setIsSidebarOpen((prev) => !prev);
|
||||
};
|
||||
|
||||
const toggleSidebar = () => {
|
||||
setIsSidebarOpen(!isSidebarOpen);
|
||||
};
|
||||
|
||||
// Add main menu items here
|
||||
const mainMenuItems = [
|
||||
{ menuItemId: { menuItemDesc: "Home" }, subMenus: [] },
|
||||
{ menuItemId: { menuItemDesc: "Transaction" }, subMenus: [] },
|
||||
{ menuItemId: { menuItemDesc: "Data Management" }, subMenus: [] },
|
||||
];
|
||||
|
||||
const renderView = () => {
|
||||
switch (currentView) {
|
||||
case "home":
|
||||
return (
|
||||
<>
|
||||
<AccordionPage />
|
||||
<BadgesPage />
|
||||
<DashViewone />
|
||||
</>
|
||||
);
|
||||
case "dashboard":
|
||||
return <DashboardView />;
|
||||
case "setup":
|
||||
return (
|
||||
<SetupView
|
||||
onUserMaintenance={() => setCurrentView("userMaintenance")}
|
||||
onMenuAccessControl={() => setCurrentView("menuAccessControl")}
|
||||
onUserGroupMaintenance={() =>
|
||||
setCurrentView("userGroupMaintenance")
|
||||
}
|
||||
onSystemParameter={() => setCurrentView("systemParameter")}
|
||||
onMenuMaintenance={() => setCurrentView("menuMaintenance")}
|
||||
onAccessType={() => setCurrentView("accessType")}
|
||||
onAPIregistry={() => setCurrentView("apiRegistry")}
|
||||
onTokenregistry={() => setCurrentView("tokenRegistry")}
|
||||
onDataType1={() => setCurrentView("dataType1")}
|
||||
onDataType2={() => setCurrentView("dataType2")}
|
||||
onDataType3={() => setCurrentView("dataType3")}
|
||||
onDataType4={() => setCurrentView("dataType4")}
|
||||
onDataType5={() => setCurrentView("dataType5")}
|
||||
onDataType6={() => setCurrentView("dataType6")}
|
||||
onDynamicTable={() => setCurrentView("dynamicTable")}
|
||||
onCodeExtension={() => setCurrentView("codeExtension")}
|
||||
/>
|
||||
);
|
||||
case "userdetails":
|
||||
return <UserDetailsView />;
|
||||
case "userMaintenance":
|
||||
return <UserMaintenanceView />;
|
||||
case "accessType":
|
||||
return <AccessType />;
|
||||
case "systemParameter":
|
||||
return <SystemParameter />;
|
||||
case "menuAccessControl":
|
||||
return <MenuAccessControl />;
|
||||
case "menuMaintenance":
|
||||
return <MenuMaintenance />;
|
||||
case "userGroupMaintenance":
|
||||
return <UserGroupMaintenance />;
|
||||
case "apiRegistry":
|
||||
return <APIRegistry />;
|
||||
case "tokenRegistry":
|
||||
return <TOKENRegistry />;
|
||||
case "dataType1":
|
||||
return <DataType1 />;
|
||||
case "dataType2":
|
||||
return <DataType2 />;
|
||||
case "dataType3":
|
||||
return <DataType3 />;
|
||||
case "dataType4":
|
||||
return <DataType4 />;
|
||||
case "dataType5":
|
||||
return <DataType5 />;
|
||||
case "dataType6":
|
||||
return <DataType6 />;
|
||||
case "dynamictable":
|
||||
return <DynamicTable />;
|
||||
case "codeExtension":
|
||||
return <CodeExtension />;
|
||||
default:
|
||||
return <div>Select an option from the menu</div>;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
{/* Navbar */}
|
||||
<div className="topbar">
|
||||
<div className="navbar-icons">
|
||||
<i
|
||||
onClick={() => setCurrentView("home")}
|
||||
className="fa-solid fa-house"
|
||||
></i>
|
||||
<i
|
||||
onClick={() => setCurrentView("dashboard")}
|
||||
className="fas fa-chart-bar"
|
||||
></i>
|
||||
<i
|
||||
onClick={() => setCurrentView("setup")}
|
||||
className="fa-solid fa-gear"
|
||||
></i>
|
||||
<i
|
||||
onClick={() => setCurrentView("userdetails")}
|
||||
className="fa-solid fa-users"
|
||||
></i>
|
||||
</div>
|
||||
|
||||
{/* User Profile Avatar */}
|
||||
<div>
|
||||
<Dropdown className="navbar-avatar">
|
||||
<Dropdown.Toggle
|
||||
variant="link"
|
||||
id="avatar-dropdown"
|
||||
bsPrefix="custom-toggle"
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={faUserCircle}
|
||||
className="user-Profile-Icon text-white"
|
||||
/>
|
||||
</Dropdown.Toggle>
|
||||
|
||||
<Dropdown.Menu>
|
||||
<Dropdown.Item onClick={() => setCurrentView("profile")}>
|
||||
<FontAwesomeIcon icon={faUserCircle} /> Profile
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item onClick={handleLogout}>
|
||||
<FontAwesomeIcon icon={faSignOutAlt} /> Logout
|
||||
</Dropdown.Item>
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Sidebar Icon (below the topbar) */}
|
||||
{/* Sidebar Icon (below the topbar and above the sidebar) */}
|
||||
<div className="sidebar-icon-container">
|
||||
{/* Slider to control the sidebar */}
|
||||
<Slider isOpen={isSidebarOpen} toggleSlider={toggleSlider} />
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{/* Sidebar */}
|
||||
|
||||
<div className={`sidebar ${isSidebarOpen ? "open" : "closed"}`}>
|
||||
<Sidebar
|
||||
menuItems={[...mainMenuItems, ...menuItems]} // Combine main and dynamic menu items
|
||||
setCurrentView={setCurrentView}
|
||||
isSidebarOpen={isSidebarOpen}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
{/* main view */}
|
||||
<div className="main-side"></div>
|
||||
|
||||
{/* Main content rendering based on current view */}
|
||||
<div className="main">{renderView()}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Dashboard;
|
||||
382
src/components/Dashboard/DashboardView.js
Normal file
382
src/components/Dashboard/DashboardView.js
Normal file
@@ -0,0 +1,382 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Modal, Button, Form, Card } from "react-bootstrap";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faEdit, faTrash } from "@fortawesome/free-solid-svg-icons";
|
||||
|
||||
function DashboardView({onDashboardCharts}) {
|
||||
|
||||
const [showTable, setShowTable] = useState(false);
|
||||
const [dashboards, setDashboards] = useState([]);
|
||||
const [showAddItemPopup, setShowAddItemPopup] = useState(false);
|
||||
const [newItem, setNewItem] = useState({
|
||||
dashboardName: "",
|
||||
description: "",
|
||||
securityProfile: "",
|
||||
addToHome: "",
|
||||
});
|
||||
const [selectedItemIndex, setSelectedItemIndex] = useState(null);
|
||||
const [columns, setColumns] = useState([
|
||||
{ label: "Dashboard Name", key: "dashboardName", visible: true },
|
||||
{ label: "Description", key: "description", visible: true },
|
||||
{ label: "Security Profile", key: "securityProfile", visible: true },
|
||||
{ label: "Add to Home", key: "addToHome", visible: true },
|
||||
]);
|
||||
const [showManageColumnsModal, setShowManageColumnsModal] = useState(false);
|
||||
const [recordsPerPage, setRecordsPerPage] = useState(10); // Initial number of records per page
|
||||
const [error, setError] = useState(null); // To handle error state
|
||||
|
||||
useEffect(() => {
|
||||
if (showTable) {
|
||||
fetchData();
|
||||
} else {
|
||||
fetchDashboardCardData();
|
||||
}
|
||||
}, [showTable]);
|
||||
|
||||
const fetchData = async () => {
|
||||
const apiUrl = `${process.env.REACT_APP_API_URL}/getNotificationByUser`;
|
||||
const token = localStorage.getItem("authToken");
|
||||
|
||||
if (!token) {
|
||||
console.error("Authorization token is missing.");
|
||||
setError("Authorization token is missing.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(apiUrl, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Error: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
setDashboards(data);
|
||||
} catch (error) {
|
||||
console.error("Fetching error:", error);
|
||||
setError(error.toString());
|
||||
}
|
||||
};
|
||||
|
||||
const fetchDashboardCardData = async () => {
|
||||
const apiUrl = `${process.env.REACT_APP_API_URL}/get_Dashboard_header`;
|
||||
const token = localStorage.getItem("authToken");
|
||||
|
||||
if (!token) {
|
||||
console.error("Authorization token is missing.");
|
||||
setError("Authorization token is missing.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(apiUrl, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Error: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
const testingDashboard = data.find(
|
||||
(dashboard) => dashboard.dashboard_name === "Testing Dashboard"
|
||||
);
|
||||
setDashboards(testingDashboard ? [testingDashboard] : []);
|
||||
} catch (error) {
|
||||
console.error("Fetching error:", error);
|
||||
setError(error.toString());
|
||||
}
|
||||
};
|
||||
|
||||
const handleAddItemClick = () => {
|
||||
setShowAddItemPopup(true);
|
||||
};
|
||||
|
||||
const handleInputChange = (event) => {
|
||||
const { name, value } = event.target;
|
||||
setNewItem({ ...newItem, [name]: value });
|
||||
};
|
||||
|
||||
const handleAddItem = () => {
|
||||
if (selectedItemIndex !== null) {
|
||||
const updatedDashboards = [...dashboards];
|
||||
updatedDashboards[selectedItemIndex] = newItem;
|
||||
setDashboards(updatedDashboards);
|
||||
} else {
|
||||
setDashboards([...dashboards, newItem]);
|
||||
}
|
||||
setNewItem({
|
||||
dashboardName: "",
|
||||
description: "",
|
||||
securityProfile: "",
|
||||
addToHome: "",
|
||||
});
|
||||
setSelectedItemIndex(null);
|
||||
setShowAddItemPopup(false);
|
||||
};
|
||||
|
||||
const handleDeleteItem = (index) => {
|
||||
const updatedDashboards = [...dashboards];
|
||||
updatedDashboards.splice(index, 1);
|
||||
setDashboards(updatedDashboards);
|
||||
};
|
||||
|
||||
const handleColumnVisibilityChange = (key) => {
|
||||
const updatedColumns = columns.map((col) => {
|
||||
if (col.key === key) {
|
||||
return { ...col, visible: !col.visible };
|
||||
}
|
||||
return col;
|
||||
});
|
||||
setColumns(updatedColumns);
|
||||
};
|
||||
|
||||
const handleRecordsPerPageChange = (value) => {
|
||||
setRecordsPerPage(value);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container mt-5">
|
||||
<h4>Dashboard</h4>
|
||||
|
||||
{/* Header Buttons */}
|
||||
<div className="text-end mb-3">
|
||||
{!showTable && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
className="me-2"
|
||||
onClick={() => setShowTable(true)}
|
||||
>
|
||||
Dashboard Builder
|
||||
</Button>
|
||||
)}
|
||||
{showTable && (
|
||||
<div className="text-end mb-3">
|
||||
<Button
|
||||
variant="secondary"
|
||||
className="me-2"
|
||||
onClick={() => setShowTable(false)}
|
||||
>
|
||||
Back
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
className="me-2"
|
||||
onClick={handleAddItemClick}
|
||||
>
|
||||
Add Item
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Render the card component only if showTable is false */}
|
||||
{!showTable && dashboards.length > 0 && (
|
||||
<div className="card-container">
|
||||
<Card className="dashboard-card col-lg-4 col-md-6 col-sm-12 mb-4" onClick={onDashboardCharts} >
|
||||
<Card.Body>
|
||||
<Card.Title>{dashboards[0].dashboard_name}</Card.Title>
|
||||
<Card.Text>{dashboards[0].description}</Card.Text>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Table */}
|
||||
{showTable && (
|
||||
<div>
|
||||
<table className="table table-striped">
|
||||
{/* Table Header */}
|
||||
<thead>
|
||||
<tr>
|
||||
{columns
|
||||
.filter((column) => column.visible)
|
||||
.map((column, index) => (
|
||||
<th key={index}>{column.label}</th>
|
||||
))}
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{/* Table Body */}
|
||||
<tbody>
|
||||
{dashboards.map((dashboard, index) => (
|
||||
<tr key={index}>
|
||||
{columns
|
||||
.filter((column) => column.visible)
|
||||
.map((column, columnIndex) => (
|
||||
<td key={columnIndex}>{dashboard[column.key]}</td>
|
||||
))}
|
||||
<td>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
className="me-2"
|
||||
onClick={() => {
|
||||
setShowAddItemPopup(true);
|
||||
setNewItem(dashboard);
|
||||
setSelectedItemIndex(index);
|
||||
}}
|
||||
>
|
||||
<FontAwesomeIcon icon={faEdit} />
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
onClick={() => handleDeleteItem(index)}
|
||||
>
|
||||
<FontAwesomeIcon icon={faTrash} />
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div className="d-flex justify-content-between">
|
||||
{/* Manage Columns Button */}
|
||||
<div className="text-start mb-3">
|
||||
<Button
|
||||
variant="secondary"
|
||||
className="me-2"
|
||||
onClick={() => setShowManageColumnsModal(true)}
|
||||
>
|
||||
Manage Columns
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Records per Page Dropdown */}
|
||||
<div className="text-end mb-3">
|
||||
<label htmlFor="recordsPerPage" className="">
|
||||
Records per Page
|
||||
</label>
|
||||
<select
|
||||
className="form-select"
|
||||
value={recordsPerPage}
|
||||
onChange={(e) => handleRecordsPerPageChange(e.target.value)}
|
||||
>
|
||||
<option value={10}>10</option>
|
||||
<option value={20}>20</option>
|
||||
<option value={50}>50</option>
|
||||
<option value={100}>100</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Add Item Modal */}
|
||||
<Modal show={showAddItemPopup} onHide={() => setShowAddItemPopup(false)}>
|
||||
<Modal.Header closeButton>
|
||||
<Modal.Title>
|
||||
{selectedItemIndex !== null ? "Edit Item" : "Add Item"}
|
||||
</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
<form>
|
||||
<div className="mb-3">
|
||||
<label htmlFor="dashboardName" className="form-label">
|
||||
Dashboard Name
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
id="dashboardName"
|
||||
name="dashboardName"
|
||||
value={newItem.dashboardName}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label htmlFor="description" className="form-label">
|
||||
Description
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
id="description"
|
||||
name="description"
|
||||
value={newItem.description}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label htmlFor="securityProfile" className="form-label">
|
||||
Security Profile
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
id="securityProfile"
|
||||
name="securityProfile"
|
||||
value={newItem.securityProfile}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label htmlFor="addToHome" className="form-label">
|
||||
Add to Home
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
id="addToHome"
|
||||
name="addToHome"
|
||||
value={newItem.addToHome}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button variant="secondary" onClick={() => setShowAddItemPopup(false)}>
|
||||
Close
|
||||
</Button>
|
||||
<Button variant="primary" onClick={handleAddItem}>
|
||||
{selectedItemIndex !== null ? "Save Changes" : "Add Item"}
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
|
||||
{/* Manage Columns Modal */}
|
||||
<Modal
|
||||
show={showManageColumnsModal}
|
||||
onHide={() => setShowManageColumnsModal(false)}
|
||||
>
|
||||
<Modal.Header closeButton>
|
||||
<Modal.Title>Manage Columns</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
<form>
|
||||
{columns.map((column, index) => (
|
||||
<div key={index} className="form-check">
|
||||
<input
|
||||
className="form-check-input"
|
||||
type="checkbox"
|
||||
checked={column.visible}
|
||||
onChange={() => handleColumnVisibilityChange(column.key)}
|
||||
/>
|
||||
<label className="form-check-label">{column.label}</label>
|
||||
</div>
|
||||
))}
|
||||
</form>
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => setShowManageColumnsModal(false)}
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default DashboardView;
|
||||
9
src/components/Dashboard/DataManagement.js
Normal file
9
src/components/Dashboard/DataManagement.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import React from 'react'
|
||||
|
||||
const DataManagement = () => {
|
||||
return (
|
||||
<div>DataManagement</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default DataManagement
|
||||
1009
src/components/Dashboard/DataType1.js
Normal file
1009
src/components/Dashboard/DataType1.js
Normal file
File diff suppressed because it is too large
Load Diff
443
src/components/Dashboard/DataType2.js
Normal file
443
src/components/Dashboard/DataType2.js
Normal file
@@ -0,0 +1,443 @@
|
||||
// import React, { useState, useEffect, useRef } from 'react';
|
||||
// import QRCode from 'qrcode.react';
|
||||
// import Barcode from 'react-barcode';
|
||||
// import { QrReader } from 'react-qr-reader';
|
||||
// import Quagga from 'quagga';
|
||||
// import { QRCodeCanvas } from 'qrcode.react'; // Updated import
|
||||
|
||||
// import 'bootstrap/dist/css/bootstrap.min.css';
|
||||
// import '../Dashboard/CSS/CSS/DataType2.css';
|
||||
|
||||
// const DataType2 = () => {
|
||||
// const [qrInput, setQrInput] = useState('');
|
||||
// const [barcodeInput, setBarcodeInput] = useState('');
|
||||
// const [image, setImage] = useState(null);
|
||||
// const [file, setFile] = useState(null);
|
||||
// const [video, setVideo] = useState(null);
|
||||
// const [audio, setAudio] = useState(null);
|
||||
// const [scanResult, setScanResult] = useState('');
|
||||
// const [barcodeResult, setBarcodeResult] = useState('');
|
||||
// const barcodeScannerRef = useRef(null);
|
||||
|
||||
// const handleQrInputChange = (e) => {
|
||||
// setQrInput(e.target.value);
|
||||
// };
|
||||
|
||||
// const handleBarcodeInputChange = (e) => {
|
||||
// setBarcodeInput(e.target.value);
|
||||
// };
|
||||
|
||||
// const handleImageChange = (e) => {
|
||||
// if (e.target.files[0]) {
|
||||
// setImage(URL.createObjectURL(e.target.files[0]));
|
||||
// }
|
||||
// };
|
||||
|
||||
// const handleFileChange = (e) => {
|
||||
// if (e.target.files[0]) {
|
||||
// setFile(e.target.files[0].name);
|
||||
// }
|
||||
// };
|
||||
|
||||
// const handleVideoChange = (e) => {
|
||||
// if (e.target.files[0]) {
|
||||
// setVideo(URL.createObjectURL(e.target.files[0]));
|
||||
// }
|
||||
// };
|
||||
|
||||
// const handleAudioChange = (e) => {
|
||||
// if (e.target.files[0]) {
|
||||
// setAudio(URL.createObjectURL(e.target.files[0]));
|
||||
// }
|
||||
// };
|
||||
|
||||
// const handleScan = (data) => {
|
||||
// if (data) {
|
||||
// setScanResult(data);
|
||||
// }
|
||||
// };
|
||||
|
||||
// const handleError = (err) => {
|
||||
// console.error(err);
|
||||
// };
|
||||
|
||||
// useEffect(() => {
|
||||
// if (barcodeScannerRef.current) {
|
||||
// Quagga.init(
|
||||
// {
|
||||
// inputStream: {
|
||||
// name: 'Live',
|
||||
// type: 'LiveStream',
|
||||
// target: barcodeScannerRef.current,
|
||||
// constraints: {
|
||||
// facingMode: 'environment',
|
||||
// },
|
||||
// },
|
||||
// decoder: {
|
||||
// readers: ['code_128_reader'],
|
||||
// },
|
||||
// },
|
||||
// function (err) {
|
||||
// if (err) {
|
||||
// console.error(err);
|
||||
// return;
|
||||
// }
|
||||
// Quagga.start();
|
||||
// }
|
||||
// );
|
||||
|
||||
// Quagga.onDetected((data) => {
|
||||
// setBarcodeResult(data.codeResult.code);
|
||||
// });
|
||||
|
||||
// return () => {
|
||||
// Quagga.offDetected();
|
||||
// Quagga.stop();
|
||||
// };
|
||||
// }
|
||||
// }, []);
|
||||
|
||||
// return (
|
||||
// <div className="container mt-5">
|
||||
// <h1 className="text-center mb-4">QR Code Generator</h1>
|
||||
// <div className="row justify-content-center mb-5">
|
||||
// <div className="col-md-6">
|
||||
// <input
|
||||
// type="text"
|
||||
// className="form-control mb-4"
|
||||
// placeholder="Enter text for QR code..."
|
||||
// value={qrInput}
|
||||
// onChange={handleQrInputChange}
|
||||
// />
|
||||
// <div className="d-flex justify-content-center">
|
||||
// <QRCode value={qrInput ? qrInput : 'https://example.com'} />
|
||||
// </div>
|
||||
// </div>
|
||||
// </div>
|
||||
|
||||
// <h1 className="text-center mb-4">Barcode Generator</h1>
|
||||
// <div className="row justify-content-center mb-5">
|
||||
// <div className="col-md-6">
|
||||
// <input
|
||||
// type="text"
|
||||
// className="form-control mb-4"
|
||||
// placeholder="Enter text for barcode..."
|
||||
// value={barcodeInput}
|
||||
// onChange={handleBarcodeInputChange}
|
||||
// />
|
||||
// <div className="d-flex justify-content-center">
|
||||
// <Barcode
|
||||
// value={barcodeInput ? barcodeInput : '1234567890'}
|
||||
// format="CODE128"
|
||||
// displayValue={true}
|
||||
// width={1}
|
||||
// height={50}
|
||||
// margin={10}
|
||||
// />
|
||||
// </div>
|
||||
// </div>
|
||||
// </div>
|
||||
|
||||
// <h1 className="text-center mb-4">Image Upload</h1>
|
||||
// <div className="row justify-content-center mb-5">
|
||||
// <div className="col-md-6">
|
||||
// <input
|
||||
// type="file"
|
||||
// className="form-control mb-4"
|
||||
// onChange={handleImageChange}
|
||||
// accept="image/*"
|
||||
// />
|
||||
// {image && <img src={image} alt="Uploaded" className="img-fluid rounded shadow" />}
|
||||
// </div>
|
||||
// </div>
|
||||
|
||||
// <h1 className="text-center mb-4">File Upload</h1>
|
||||
// <div className="row justify-content-center mb-5">
|
||||
// <div className="col-md-6">
|
||||
// <input
|
||||
// type="file"
|
||||
// className="form-control mb-4"
|
||||
// onChange={handleFileChange}
|
||||
// />
|
||||
// {file && <p className="uploaded-file">Uploaded File: {file}</p>}
|
||||
// </div>
|
||||
// </div>
|
||||
|
||||
// <h1 className="text-center mb-4">Video Upload</h1>
|
||||
// <div className="row justify-content-center mb-5">
|
||||
// <div className="col-md-6">
|
||||
// <input
|
||||
// type="file"
|
||||
// className="form-control mb-4"
|
||||
// onChange={handleVideoChange}
|
||||
// accept="video/*"
|
||||
// />
|
||||
// {video && <video src={video} controls className="img-fluid rounded shadow" />}
|
||||
// </div>
|
||||
// </div>
|
||||
|
||||
// <h1 className="text-center mb-4">Audio Upload</h1>
|
||||
// <div className="row justify-content-center mb-5">
|
||||
// <div className="col-md-6">
|
||||
// <input
|
||||
// type="file"
|
||||
// className="form-control mb-4"
|
||||
// onChange={handleAudioChange}
|
||||
// accept="audio/*"
|
||||
// />
|
||||
// {audio && <audio src={audio} controls className="w-100" />}
|
||||
// </div>
|
||||
// </div>
|
||||
|
||||
// <h1 className="text-center mb-4">QR Code Scanner</h1>
|
||||
// <div className="row justify-content-center mb-5">
|
||||
// <div className="col-md-6">
|
||||
// <QrReader
|
||||
// delay={300}
|
||||
// onError={handleError}
|
||||
// onScan={handleScan}
|
||||
// style={{ width: '100%' }}
|
||||
// />
|
||||
// {scanResult && <p className="scanned-result">Scanned Result: {scanResult}</p>}
|
||||
// </div>
|
||||
// </div>
|
||||
|
||||
// <h1 className="text-center mb-4">Barcode Scanner</h1>
|
||||
// <div className="row justify-content-center mb-5">
|
||||
// <div className="col-md-6">
|
||||
// <div ref={barcodeScannerRef} className="barcode-scanner"></div>
|
||||
// {barcodeResult && <p className="scanned-result">Last Scanned Barcode: {barcodeResult}</p>}
|
||||
// </div>
|
||||
// </div>
|
||||
// </div>
|
||||
// );
|
||||
// };
|
||||
|
||||
// export default DataType2;
|
||||
|
||||
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { QRCodeCanvas } from 'qrcode.react'; // Use QRCodeCanvas instead of default import
|
||||
import Barcode from 'react-barcode';
|
||||
import { QrReader } from 'react-qr-reader';
|
||||
import Quagga from 'quagga';
|
||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||
import '../Dashboard/CSS/CSS/DataType2.css';
|
||||
import { toast } from "react-toastify";
|
||||
import Spinner from '../../UIComponants/Spinner';
|
||||
|
||||
|
||||
const DataType2 = () => {
|
||||
const [qrInput, setQrInput] = useState('');
|
||||
const [barcodeInput, setBarcodeInput] = useState('');
|
||||
const [image, setImage] = useState(null);
|
||||
const [file, setFile] = useState(null);
|
||||
const [video, setVideo] = useState(null);
|
||||
const [audio, setAudio] = useState(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [scanResult, setScanResult] = useState('');
|
||||
const [barcodeResult, setBarcodeResult] = useState('');
|
||||
const barcodeScannerRef = useRef(null);
|
||||
|
||||
const handleQrInputChange = (e) => setQrInput(e.target.value);
|
||||
const handleBarcodeInputChange = (e) => setBarcodeInput(e.target.value);
|
||||
|
||||
const handleScan = (data) => data && setScanResult(data);
|
||||
const handleError = (err) => console.error(err);
|
||||
|
||||
|
||||
const handleImageChange = (e) => {
|
||||
if (e.target.files[0]) {
|
||||
setImage(URL.createObjectURL(e.target.files[0]));
|
||||
toast.success("Image uploaded successfully!");
|
||||
}
|
||||
};
|
||||
|
||||
const handleFileChange = (e) => {
|
||||
if (e.target.files[0]) {
|
||||
setFile(e.target.files[0].name);
|
||||
toast.success("File uploaded successfully!");
|
||||
}
|
||||
};
|
||||
|
||||
const handleVideoChange = (e) => {
|
||||
if (e.target.files[0]) {
|
||||
setVideo(URL.createObjectURL(e.target.files[0]));
|
||||
toast.success("Video uploaded successfully!");
|
||||
}
|
||||
};
|
||||
|
||||
const handleAudioChange = (e) => {
|
||||
if (e.target.files[0]) {
|
||||
setAudio(URL.createObjectURL(e.target.files[0]));
|
||||
toast.success("Audio uploaded successfully!");
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
// Simulate loading data
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
}, 3000); // Simulated 3 seconds loading
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (barcodeScannerRef.current) {
|
||||
Quagga.init(
|
||||
{
|
||||
inputStream: {
|
||||
name: 'Live',
|
||||
type: 'LiveStream',
|
||||
target: barcodeScannerRef.current,
|
||||
constraints: {
|
||||
facingMode: 'environment',
|
||||
},
|
||||
},
|
||||
decoder: {
|
||||
readers: ['code_128_reader'],
|
||||
},
|
||||
},
|
||||
(err) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
return;
|
||||
}
|
||||
Quagga.start();
|
||||
}
|
||||
);
|
||||
|
||||
Quagga.onDetected((data) => {
|
||||
setBarcodeResult(data.codeResult.code);
|
||||
});
|
||||
|
||||
return () => {
|
||||
Quagga.offDetected();
|
||||
Quagga.stop();
|
||||
};
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div style={{marginTop:"11rem"}}>
|
||||
{loading ? (
|
||||
<Spinner/>
|
||||
):(
|
||||
<div className="container mt-5">
|
||||
<h1 className="text-center mb-4">QR Code Generator</h1>
|
||||
<div className="row justify-content-center mb-5">
|
||||
<div className="col-md-6">
|
||||
<input
|
||||
type="text"
|
||||
className="form-control mb-4 custom-hover-border"
|
||||
placeholder="Enter text for QR code..."
|
||||
value={qrInput}
|
||||
onChange={handleQrInputChange}
|
||||
/>
|
||||
<div className="d-flex justify-content-center">
|
||||
<QRCodeCanvas value={qrInput || 'https://example.com'} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1 className="text-center mb-4">Barcode Generator</h1>
|
||||
<div className="row justify-content-center mb-5">
|
||||
<div className="col-md-6">
|
||||
<input
|
||||
type="text"
|
||||
className="form-control mb-4 custom-hover-border"
|
||||
placeholder="Enter text for barcode..."
|
||||
value={barcodeInput}
|
||||
onChange={handleBarcodeInputChange}
|
||||
/>
|
||||
<div className="d-flex justify-content-center">
|
||||
<Barcode
|
||||
value={barcodeInput || '1234567890'}
|
||||
format="CODE128"
|
||||
displayValue
|
||||
width={1}
|
||||
height={50}
|
||||
margin={10}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<h1 className="text-center mb-4">Image Upload</h1>
|
||||
<div className="row justify-content-center mb-5">
|
||||
<div className="col-md-6">
|
||||
<input
|
||||
type="file"
|
||||
className="custom-file-input"
|
||||
id="customFileImage"
|
||||
onChange={handleImageChange}
|
||||
accept="image/*"
|
||||
/>
|
||||
<label htmlFor="customFileImage" className="custom-file-label">
|
||||
Choose File
|
||||
</label>
|
||||
{image && <img src={image} alt="Uploaded" className="img-fluid rounded shadow mt-3" />}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1 className="text-center mb-4">Video Upload</h1>
|
||||
<div className="row justify-content-center mb-5">
|
||||
<div className="col-md-6">
|
||||
<input
|
||||
type="file"
|
||||
className="custom-file-input "
|
||||
id="customFileVideo"
|
||||
onChange={handleVideoChange}
|
||||
accept="video/*"
|
||||
/>
|
||||
<label htmlFor="customFileVideo" className="custom-file-label ">
|
||||
Choose File
|
||||
</label>
|
||||
{video && <video src={video} controls className="img-fluid rounded shadow mt-3" />}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1 className="text-center mb-4">Audio Upload</h1>
|
||||
<div className="row justify-content-center mb-5">
|
||||
<div className="col-md-6">
|
||||
<input
|
||||
type="file"
|
||||
className="custom-file-input custom-hover-border"
|
||||
id="customFileAudio"
|
||||
onChange={handleAudioChange}
|
||||
accept="audio/*"
|
||||
/>
|
||||
<label htmlFor="customFileAudio" className="custom-file-label">
|
||||
Choose File
|
||||
</label>
|
||||
{audio && <audio src={audio} controls className="w-100 mt-3" />}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1 className="text-center mb-4">QR Code Scanner</h1>
|
||||
<div className="row justify-content-center mb-5">
|
||||
<div className="col-md-6">
|
||||
<QrReader
|
||||
delay={300}
|
||||
onError={handleError}
|
||||
onScan={handleScan}
|
||||
style={{ width: '100%' }}
|
||||
/>
|
||||
{scanResult && <p className="scanned-result">Scanned Result: {scanResult}</p>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1 className="text-center mb-4">Barcode Scanner</h1>
|
||||
<div className="row justify-content-center mb-5">
|
||||
<div className="col-md-6">
|
||||
<div ref={barcodeScannerRef} className="barcode-scanner"></div>
|
||||
{barcodeResult && <p className="scanned-result">Last Scanned Barcode: {barcodeResult}</p>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
export default DataType2;
|
||||
76
src/components/Dashboard/DataType3.js
Normal file
76
src/components/Dashboard/DataType3.js
Normal file
@@ -0,0 +1,76 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||
import Spinner from '../../UIComponants/Spinner';
|
||||
|
||||
function DataType3() {
|
||||
// State for the menu items and the selected menu item
|
||||
const [menuItems, setMenuItems] = useState([]);
|
||||
const [selectedMenuItem, setSelectedMenuItem] = useState('');
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const response = await fetch('http://localhost:9292/fndMenu/menuloadbyuser', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Authorization': 'Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzeXNhZG1pbiIsInNjb3BlcyI6IlJPTEVfRGV2ZWxvcGVyLFJPTEVfQURNSU4iLCJpYXQiOjE3MTYzNDkyNTEsImV4cCI6MTcxODk0MTI1MX0.fLW0-Uvb6X1fLQNzdiNiYeHO3lMYa1bsFAmOJkGCdRY'
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
setMenuItems(data);
|
||||
setLoading(false);
|
||||
} catch (err) {
|
||||
setError(err.message);
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate loading data
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
}, 3000); // Simulated 3 seconds loading
|
||||
}, []);
|
||||
|
||||
const handleSelectionChange = (event) => {
|
||||
setSelectedMenuItem(event.target.value);
|
||||
};
|
||||
|
||||
// if (loading) return <div className="d-flex justify-content-center"><div className="spinner-border" role="status"><span className="visually-hidden">Loading...</span></div></div>;
|
||||
if (loading) return <div>Loading...</div>;
|
||||
if (error) return <div>Error: {error}</div>;
|
||||
|
||||
|
||||
return (
|
||||
<div style={{marginTop:"11rem"}}>
|
||||
{loading ? (
|
||||
<Spinner/>
|
||||
):(
|
||||
<div>
|
||||
<h5>Select a Menu Item</h5>
|
||||
<select value={selectedMenuItem} onChange={handleSelectionChange}>
|
||||
<option value="">Select an Option</option>
|
||||
{menuItems.map((item) => (
|
||||
<option key={item.menuItemId.menuItemId} value={item.menuItemId.menuItemId}>
|
||||
{item.menuItemDesc}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
export default DataType3;
|
||||
354
src/components/Dashboard/DataType4.js
Normal file
354
src/components/Dashboard/DataType4.js
Normal file
@@ -0,0 +1,354 @@
|
||||
// import React, { useState } from 'react';
|
||||
// import 'bootstrap/dist/css/bootstrap.min.css';
|
||||
|
||||
// function DataType4() {
|
||||
// const [paymentData, setPaymentData] = useState({
|
||||
// cardNumber: '',
|
||||
// cardHolder: '',
|
||||
// expiryDate: '',
|
||||
// cvv: ''
|
||||
// });
|
||||
|
||||
// const handleChange = (e) => {
|
||||
// const { name, value } = e.target;
|
||||
// setPaymentData(prevData => ({
|
||||
// ...prevData,
|
||||
// [name]: value
|
||||
// }));
|
||||
// };
|
||||
|
||||
// const handleSubmit = (event) => {
|
||||
// event.preventDefault();
|
||||
// alert("Form submitted successfully!");
|
||||
// console.log("Form Data:", paymentData);
|
||||
// };
|
||||
|
||||
// return (
|
||||
// <div className="container mt-3">
|
||||
// <h2>Payment Details</h2>
|
||||
// <form onSubmit={handleSubmit} className="row g-3">
|
||||
// <div className="col-md-6">
|
||||
// <label htmlFor="cardNumber" className="form-label">Card Number</label>
|
||||
// <input
|
||||
// type="text"
|
||||
// className="form-control"
|
||||
// id="cardNumber"
|
||||
// name="cardNumber"
|
||||
// value={paymentData.cardNumber}
|
||||
// onChange={handleChange}
|
||||
// pattern="\d{16}"
|
||||
// title="Card number must be 16 digits"
|
||||
// style={{ borderColor: "#343a40" }}
|
||||
// required
|
||||
// />
|
||||
// </div>
|
||||
// <div className="col-md-6">
|
||||
// <label htmlFor="cardHolder" className="form-label">Card Holder's Name</label>
|
||||
// <input
|
||||
// type="text"
|
||||
// className="form-control"
|
||||
// id="cardHolder"
|
||||
// name="cardHolder"
|
||||
// value={paymentData.cardHolder}
|
||||
// onChange={handleChange}
|
||||
// style={{ borderColor: "#343a40" }}
|
||||
// required
|
||||
// />
|
||||
// </div>
|
||||
// <div className="col-md-6">
|
||||
// <label htmlFor="expiryDate" className="form-label">Expiry Date</label>
|
||||
// <input
|
||||
// type="month"
|
||||
// className="form-control"
|
||||
// id="expiryDate"
|
||||
// name="expiryDate"
|
||||
// value={paymentData.expiryDate}
|
||||
// style={{ borderColor: "#343a40" }}
|
||||
// onChange={handleChange}
|
||||
// required
|
||||
// />
|
||||
// </div>
|
||||
// <div className="col-md-6">
|
||||
// <label htmlFor="cvv" className="form-label">CVV</label>
|
||||
// <input
|
||||
// type="text"
|
||||
// className="form-control"
|
||||
// id="cvv"
|
||||
// name="cvv"
|
||||
// value={paymentData.cvv}
|
||||
// onChange={handleChange}
|
||||
// style={{ borderColor: "#343a40" }}
|
||||
// pattern="\d{3}"
|
||||
// title="CVV must be 3 digits"
|
||||
// required
|
||||
// />
|
||||
// </div>
|
||||
// <div className="col-12">
|
||||
// <button type="submit" size="sm" className="btn btn-primary">Submit Payment</button>
|
||||
// </div>
|
||||
// </form>
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
|
||||
// export default DataType4;
|
||||
|
||||
// import React, { useState } from 'react';
|
||||
// import 'bootstrap/dist/css/bootstrap.min.css';
|
||||
// import { FaCreditCard, FaUser, FaLock } from 'react-icons/fa'; // Import specific icons
|
||||
// import "../Dashboard/CSS/CSS/CommonStyle.css";
|
||||
// function DataType4() {
|
||||
// const [paymentData, setPaymentData] = useState({
|
||||
// cardNumber: '',
|
||||
// cardHolder: '',
|
||||
// expiryDate: '',
|
||||
// cvv: ''
|
||||
// });
|
||||
|
||||
// const handleChange = (e) => {
|
||||
// const { name, value } = e.target;
|
||||
// setPaymentData(prevData => ({
|
||||
// ...prevData,
|
||||
// [name]: value
|
||||
// }));
|
||||
// };
|
||||
|
||||
// const handleSubmit = (event) => {
|
||||
// event.preventDefault();
|
||||
// alert("Form submitted successfully!");
|
||||
// console.log("Form Data:", paymentData);
|
||||
// };
|
||||
|
||||
// return (
|
||||
// <div className="container mt-5">
|
||||
// <div className="card p-4 shadow-sm rounded">
|
||||
// <h2 className=" payment_details text-center mb-4 text-primary">Payment Details</h2>
|
||||
// <form onSubmit={handleSubmit} className="row g-3">
|
||||
// <div className="col-md-6">
|
||||
// <label htmlFor="cardNumber" className="form-label">Card Number</label>
|
||||
// <div className="input-group">
|
||||
// <span className="input-group-text"><FaCreditCard /></span>
|
||||
// <input
|
||||
// type="text"
|
||||
// className="form-control"
|
||||
// id="cardNumber"
|
||||
// name="cardNumber"
|
||||
// value={paymentData.cardNumber}
|
||||
// onChange={handleChange}
|
||||
// placeholder="1234 5678 9123 4567"
|
||||
// pattern="\d{16}"
|
||||
// title="Card number must be 16 digits"
|
||||
// required
|
||||
// />
|
||||
// </div>
|
||||
// </div>
|
||||
// <div className="col-md-6">
|
||||
// <label htmlFor="cardHolder" className="form-label">Card Holder's Name</label>
|
||||
// <div className="input-group">
|
||||
// <span className="input-group-text"><FaUser /></span>
|
||||
// <input
|
||||
// type="text"
|
||||
// className="form-control"
|
||||
// id="cardHolder"
|
||||
// name="cardHolder"
|
||||
// value={paymentData.cardHolder}
|
||||
// onChange={handleChange}
|
||||
// placeholder="John Doe"
|
||||
// required
|
||||
// />
|
||||
// </div>
|
||||
// </div>
|
||||
// <div className="col-md-6">
|
||||
// <label htmlFor="expiryDate" className="form-label">Expiry Date</label>
|
||||
// <input
|
||||
// type="month"
|
||||
// className="form-control"
|
||||
// id="expiryDate"
|
||||
// name="expiryDate"
|
||||
// value={paymentData.expiryDate}
|
||||
// onChange={handleChange}
|
||||
// required
|
||||
// />
|
||||
// </div>
|
||||
// <div className="col-md-6">
|
||||
// <label htmlFor="cvv" className="form-label">CVV</label>
|
||||
// <div className="input-group">
|
||||
// <span className="input-group-text"><FaLock /></span>
|
||||
// <input
|
||||
// type="text"
|
||||
// className="form-control"
|
||||
// id="cvv"
|
||||
// name="cvv"
|
||||
// value={paymentData.cvv}
|
||||
// onChange={handleChange}
|
||||
// placeholder="***"
|
||||
// pattern="\d{3}"
|
||||
// title="CVV must be 3 digits"
|
||||
// required
|
||||
// />
|
||||
// </div>
|
||||
// </div>
|
||||
// <div className="col-12 text-center">
|
||||
// <button type="submit" className="btn btn-primary btn-lg">Submit Payment</button>
|
||||
// </div>
|
||||
// </form>
|
||||
// </div>
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
|
||||
// export default DataType4;
|
||||
|
||||
|
||||
import React, { useState,useEffect } from 'react';
|
||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||
import { FaCreditCard, FaUser, FaLock } from 'react-icons/fa'; // Import specific icons
|
||||
import "../Dashboard/CSS/CSS/CommonStyle.css";
|
||||
import { toast } from "react-toastify";
|
||||
import Spinner from '../../UIComponants/Spinner';
|
||||
|
||||
function DataType4() {
|
||||
const [paymentData, setPaymentData] = useState({
|
||||
cardNumber: '',
|
||||
cardHolder: '',
|
||||
expiryDate: '',
|
||||
cvv: ''
|
||||
});
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate loading data
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
}, 3000); // Simulated 3 seconds loading
|
||||
}, []);
|
||||
|
||||
const handleChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
setPaymentData(prevData => ({
|
||||
...prevData,
|
||||
[name]: value
|
||||
}));
|
||||
};
|
||||
|
||||
const handleSubmit = (event) => {
|
||||
event.preventDefault();
|
||||
alert("Form submitted successfully!");
|
||||
console.log("Form Data:", paymentData);
|
||||
toast.success("Payment Details submitted....");
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{marginTop:"11rem"}}>
|
||||
{loading ? (
|
||||
<Spinner/>
|
||||
):(
|
||||
<div className="container mt-5">
|
||||
<div className="card p-4 shadow-lg rounded border-0">
|
||||
<h2 className="text-center mb-4 title_main" style={{ color: '#0E6591', fontWeight: 'bold' }}>
|
||||
Payment Details
|
||||
</h2>
|
||||
<form onSubmit={handleSubmit} className="row g-4">
|
||||
<div className="col-md-6">
|
||||
<label htmlFor="cardNumber" className="form-label fw-semibold">
|
||||
Card Number
|
||||
</label>
|
||||
<div className="input-group">
|
||||
<span className="input-group-text bg-light text-primary">
|
||||
<FaCreditCard />
|
||||
</span>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control shadow-sm"
|
||||
id="cardNumber"
|
||||
name="cardNumber"
|
||||
value={paymentData.cardNumber}
|
||||
onChange={handleChange}
|
||||
placeholder="1234 5678 9123 4567"
|
||||
pattern="\d{16}"
|
||||
title="Card number must be 16 digits"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<label htmlFor="cardHolder" className="form-label fw-semibold">
|
||||
Card Holder's Name
|
||||
</label>
|
||||
<div className="input-group">
|
||||
<span className="input-group-text bg-light text-primary">
|
||||
<FaUser />
|
||||
</span>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control shadow-sm"
|
||||
id="cardHolder"
|
||||
name="cardHolder"
|
||||
value={paymentData.cardHolder}
|
||||
onChange={handleChange}
|
||||
placeholder="John Doe"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<label htmlFor="expiryDate" className="form-label fw-semibold">
|
||||
Expiry Date
|
||||
</label>
|
||||
<input
|
||||
type="month"
|
||||
className="form-control shadow-sm"
|
||||
id="expiryDate"
|
||||
name="expiryDate"
|
||||
value={paymentData.expiryDate}
|
||||
onChange={handleChange}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<label htmlFor="cvv" className="form-label fw-semibold">
|
||||
CVV
|
||||
</label>
|
||||
<div className="input-group">
|
||||
<span className="input-group-text bg-light text-primary">
|
||||
<FaLock />
|
||||
</span>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control shadow-sm"
|
||||
id="cvv"
|
||||
name="cvv"
|
||||
value={paymentData.cvv}
|
||||
onChange={handleChange}
|
||||
placeholder="***"
|
||||
pattern="\d{3}"
|
||||
title="CVV must be 3 digits"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 text-center">
|
||||
<button
|
||||
type="submit"
|
||||
className="btn btn-primary btn-lg shadow-sm"
|
||||
style={{
|
||||
background: 'linear-gradient(90deg, #0E6591, #0B4C6A)',
|
||||
border: 'none',
|
||||
color: '#fff',
|
||||
marginTop:'1rem'
|
||||
}}
|
||||
>
|
||||
Submit Payment
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
export default DataType4;
|
||||
216
src/components/Dashboard/DataType5.js
Normal file
216
src/components/Dashboard/DataType5.js
Normal file
@@ -0,0 +1,216 @@
|
||||
// import React, { useState } from 'react';
|
||||
// import 'bootstrap/dist/css/bootstrap.min.css';
|
||||
// import "../Dashboard/CSS/CSS/CommonStyle.css";
|
||||
|
||||
// function DataType5() {
|
||||
// const [firstName, setFirstName] = useState('');
|
||||
// const [secondName, setSecondName] = useState('');
|
||||
// const [description, setDescription] = useState('');
|
||||
|
||||
// const handleFirstNameChange = (e) => {
|
||||
// setFirstName(e.target.value);
|
||||
// };
|
||||
|
||||
// const handleSecondNameChange = (e) => {
|
||||
// setSecondName(e.target.value);
|
||||
// };
|
||||
|
||||
// const handleDescriptionChange = (e) => {
|
||||
// setDescription(e.target.value);
|
||||
// };
|
||||
|
||||
// const handleSubmit = (e) => {
|
||||
// e.preventDefault();
|
||||
|
||||
// const formData = {
|
||||
// Name: firstName,
|
||||
// test2 : {Name: secondName, description: description }
|
||||
// };
|
||||
// console.log(JSON.stringify(formData));
|
||||
// };
|
||||
|
||||
// return (
|
||||
// <div className="container mt-4" style={{ width: '50%' }}>
|
||||
// <div className='card p-4 shadow-sm rounded'>
|
||||
// <form onSubmit={handleSubmit}>
|
||||
// <div className="form-group">
|
||||
// <label htmlFor="first-name" className="form-label"> Name:</label>
|
||||
// <input
|
||||
// type="text"
|
||||
// id="first-name"
|
||||
// name="first-name"
|
||||
// className="form-control"
|
||||
// value={firstName}
|
||||
// onChange={handleFirstNameChange}
|
||||
// placeholder="Enter your first name"
|
||||
// />
|
||||
// </div>
|
||||
// <h2 className=" title_main mt-3 text-primary" >Test 2</h2>
|
||||
// <div className="form_content">
|
||||
// <div className="form-group">
|
||||
// <label htmlFor="second-name" className="form-label"> Name:</label>
|
||||
// <input
|
||||
// type="text"
|
||||
// id="second-name"
|
||||
// name="second-name"
|
||||
// className="form-control"
|
||||
// value={secondName}
|
||||
// onChange={handleSecondNameChange}
|
||||
// placeholder="Enter your second name"
|
||||
// />
|
||||
// </div>
|
||||
// <div className="mb-3">
|
||||
// <label htmlFor="description" className="form-label">Description:</label>
|
||||
// <textarea
|
||||
// id="description"
|
||||
// name="description"
|
||||
// className="form-control"
|
||||
// rows="4"
|
||||
// value={description}
|
||||
// onChange={handleDescriptionChange}
|
||||
// placeholder="Describe yourself"
|
||||
// ></textarea>
|
||||
// </div>
|
||||
// </div>
|
||||
|
||||
// <button type="submit" className="btn btn-primary">Submit</button>
|
||||
// </form>
|
||||
// </div>
|
||||
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
|
||||
// export default DataType5;
|
||||
|
||||
|
||||
import React, { useState,useEffect } from 'react';
|
||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||
import "../Dashboard/CSS/CSS/CommonStyle.css";
|
||||
import Spinner from '../../UIComponants/Spinner';
|
||||
|
||||
function DataType5() {
|
||||
const [firstName, setFirstName] = useState('');
|
||||
const [secondName, setSecondName] = useState('');
|
||||
const [description, setDescription] = useState('');
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate loading data
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
}, 3000); // Simulated 3 seconds loading
|
||||
}, []);
|
||||
const handleFirstNameChange = (e) => {
|
||||
setFirstName(e.target.value);
|
||||
};
|
||||
|
||||
const handleSecondNameChange = (e) => {
|
||||
setSecondName(e.target.value);
|
||||
};
|
||||
|
||||
const handleDescriptionChange = (e) => {
|
||||
setDescription(e.target.value);
|
||||
};
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const formData = {
|
||||
Name: firstName,
|
||||
test2: { Name: secondName, description: description },
|
||||
};
|
||||
console.log(JSON.stringify(formData));
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{marginTop:"11rem"}}>
|
||||
{loading ? (
|
||||
<Spinner/>
|
||||
):(
|
||||
<div className="container mt-5 mb-5" style={{ maxWidth: '600px' }}>
|
||||
<div className="card p-4 shadow-lg border-0 rounded">
|
||||
<h2 className="text-center mb-4 title_main" style={{ fontWeight: 'bold' }}>
|
||||
Main Details
|
||||
</h2>
|
||||
<form onSubmit={handleSubmit}>
|
||||
{/* First Name Field */}
|
||||
<div className="form-group mb-3">
|
||||
<label htmlFor="first-name" className="form-label fw-semibold">
|
||||
First Name:
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="first-name"
|
||||
name="first-name"
|
||||
className="form-control shadow-sm custom-hover-border"
|
||||
value={firstName}
|
||||
onChange={handleFirstNameChange}
|
||||
placeholder="Enter your name"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Section Header */}
|
||||
<h3 className="mt-4 mb-3 title_main" style={{ fontWeight: 'bold' }}>
|
||||
Test 2
|
||||
</h3>
|
||||
|
||||
{/* Second Name Field */}
|
||||
<div className="form-group mb-3">
|
||||
<label htmlFor="second-name" className="form-label fw-semibold">
|
||||
Second Name:
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="second-name"
|
||||
name="second-name"
|
||||
className="form-control shadow-sm custom-hover-border"
|
||||
value={secondName}
|
||||
onChange={handleSecondNameChange}
|
||||
placeholder="Enter your second name"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Description Field */}
|
||||
<div className="form-group mb-4">
|
||||
<label htmlFor="description" className="form-label fw-semibold">
|
||||
Description:
|
||||
</label>
|
||||
<textarea
|
||||
id="description"
|
||||
name="description"
|
||||
className="form-control shadow-sm custom-hover-border"
|
||||
rows="4"
|
||||
value={description}
|
||||
onChange={handleDescriptionChange}
|
||||
placeholder="Write a brief description about yourself"
|
||||
required
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
{/* Submit Button */}
|
||||
<div className="text-center">
|
||||
<button
|
||||
type="submit"
|
||||
className="btn btn-primary btn-lg shadow-sm"
|
||||
style={{
|
||||
background: 'linear-gradient(90deg, #0E6591, #0B4C6A)',
|
||||
border: 'none',
|
||||
color: '#fff',
|
||||
}}
|
||||
>
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
export default DataType5;
|
||||
241
src/components/Dashboard/DataType6.js
Normal file
241
src/components/Dashboard/DataType6.js
Normal file
@@ -0,0 +1,241 @@
|
||||
// import React, { useState } from 'react';
|
||||
// import 'bootstrap/dist/css/bootstrap.min.css';
|
||||
// import "../Dashboard/CSS/CSS/CommonStyle.css";
|
||||
|
||||
// function DataType6() {
|
||||
// const [firstName, setFirstName] = useState('');
|
||||
// const [additionalEntries, setAdditionalEntries] = useState([]);
|
||||
|
||||
// const handleFirstNameChange = (e) => {
|
||||
// setFirstName(e.target.value);
|
||||
// };
|
||||
|
||||
// const handleAddFields = () => {
|
||||
// setAdditionalEntries([...additionalEntries, { name: '', description: '' }]);
|
||||
// };
|
||||
|
||||
// const handleNameChange = (index, e) => {
|
||||
// const updatedEntries = [...additionalEntries];
|
||||
// updatedEntries[index].name = e.target.value;
|
||||
// setAdditionalEntries(updatedEntries);
|
||||
// };
|
||||
|
||||
// const handleDescriptionChange = (index, e) => {
|
||||
// const updatedEntries = [...additionalEntries];
|
||||
// updatedEntries[index].description = e.target.value;
|
||||
// setAdditionalEntries(updatedEntries);
|
||||
// };
|
||||
|
||||
// const handleSubmit = (e) => {
|
||||
// e.preventDefault();
|
||||
// const formData = {
|
||||
// Name: firstName,
|
||||
// test2: additionalEntries
|
||||
// };
|
||||
// console.log(JSON.stringify(formData));
|
||||
// };
|
||||
|
||||
// return (
|
||||
|
||||
// <div className="container mt-4" style={{ width: '50%' }}>
|
||||
// <div className='card p-4 shadow-sm rounded'>
|
||||
// <form onSubmit={handleSubmit}>
|
||||
// <div className="form-group">
|
||||
// <label htmlFor="first-name" className="form-label">Name:</label>
|
||||
// <input
|
||||
// type="text"
|
||||
// id="first-name"
|
||||
// name="First-name"
|
||||
// className="form-control"
|
||||
// value={firstName}
|
||||
// onChange={handleFirstNameChange}
|
||||
// />
|
||||
// </div>
|
||||
// <h2 className="title_main mt-3 text-primary">Test 2</h2>
|
||||
// {additionalEntries.map((entry, index) => (
|
||||
// <div className="form_content" key={index}>
|
||||
// <div className="form-group">
|
||||
// <label htmlFor={`additional-name-${index}`} className="form-label">Name:</label>
|
||||
// <input
|
||||
// type="text"
|
||||
// id={`additional-name-${index}`}
|
||||
// name={`additional-name-${index}`}
|
||||
// className="form-control"
|
||||
// value={entry.name}
|
||||
// onChange={(e) => handleNameChange(index, e)}
|
||||
// />
|
||||
// </div>
|
||||
// <div className="form-group">
|
||||
// <label htmlFor={`additional-description-${index}`} className="form-label">Description:</label>
|
||||
// <textarea
|
||||
// id={`additional-description-${index}`}
|
||||
// name={`additional-description-${index}`}
|
||||
// className="form-control"
|
||||
// rows="4"
|
||||
// value={entry.description}
|
||||
// onChange={(e) => handleDescriptionChange(index, e)}
|
||||
// ></textarea>
|
||||
// </div>
|
||||
// </div>
|
||||
// ))}
|
||||
// <div className="btn-group mt-3">
|
||||
// <button type="submit" className="btn btn-primary me-3">Submit</button>
|
||||
// <button type="button" className="btn btn-primary" onClick={handleAddFields}>Add Test2</button>
|
||||
// </div>
|
||||
// </form>
|
||||
// </div>
|
||||
|
||||
// </div>
|
||||
|
||||
// );
|
||||
// }
|
||||
|
||||
// export default DataType6;
|
||||
|
||||
|
||||
import React, { useState,useEffect } from 'react';
|
||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||
import "../Dashboard/CSS/CSS/CommonStyle.css";
|
||||
import Spinner from '../../UIComponants/Spinner';
|
||||
|
||||
function DataType6() {
|
||||
const [firstName, setFirstName] = useState('');
|
||||
const [additionalEntries, setAdditionalEntries] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate loading data
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
}, 3000); // Simulated 3 seconds loading
|
||||
}, []);
|
||||
|
||||
const handleFirstNameChange = (e) => {
|
||||
setFirstName(e.target.value);
|
||||
};
|
||||
|
||||
const handleAddFields = () => {
|
||||
setAdditionalEntries([...additionalEntries, { name: '', description: '' }]);
|
||||
};
|
||||
|
||||
const handleNameChange = (index, e) => {
|
||||
const updatedEntries = [...additionalEntries];
|
||||
updatedEntries[index].name = e.target.value;
|
||||
setAdditionalEntries(updatedEntries);
|
||||
};
|
||||
|
||||
const handleDescriptionChange = (index, e) => {
|
||||
const updatedEntries = [...additionalEntries];
|
||||
updatedEntries[index].description = e.target.value;
|
||||
setAdditionalEntries(updatedEntries);
|
||||
};
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
const formData = {
|
||||
Name: firstName,
|
||||
test2: additionalEntries,
|
||||
};
|
||||
console.log(JSON.stringify(formData));
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{marginTop:"11rem"}}>
|
||||
{loading ? (
|
||||
<Spinner/>
|
||||
):(
|
||||
<div className="container mt-5" style={{ maxWidth: '700px' }}>
|
||||
<div className="card p-4 shadow-lg border-0 rounded">
|
||||
<h2 className="text-center mb-4 title_main" style={{ fontWeight: 'bold' }}>
|
||||
Test 2
|
||||
</h2>
|
||||
<form onSubmit={handleSubmit}>
|
||||
{/* First Name Field */}
|
||||
<div className="form-group mb-3">
|
||||
<label htmlFor="first-name" className="form-label fw-semibold">
|
||||
First Name:
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="first-name"
|
||||
name="first-name"
|
||||
className="form-control shadow-sm custom-hover-border"
|
||||
value={firstName}
|
||||
onChange={handleFirstNameChange}
|
||||
placeholder="Enter your name"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Section Header */}
|
||||
<h3 className="mt-4 mb-3 title_main" style={{ fontWeight: 'bold' }}>
|
||||
Add Test
|
||||
</h3>
|
||||
|
||||
{/* Dynamic Fields */}
|
||||
{additionalEntries.map((entry, index) => (
|
||||
<div className="p-3 mb-3 border rounded shadow-sm" key={index}>
|
||||
<div className="form-group mb-3">
|
||||
<label htmlFor={`additional-name-${index}`} className="form-label fw-semibold">
|
||||
Name:
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id={`additional-name-${index}`}
|
||||
name={`additional-name-${index}`}
|
||||
className="form-control shadow-sm custom-hover-border"
|
||||
value={entry.name}
|
||||
onChange={(e) => handleNameChange(index, e)}
|
||||
placeholder="Enter additional name"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group mb-3">
|
||||
<label htmlFor={`additional-description-${index}`} className="form-label fw-semibold">
|
||||
Description:
|
||||
</label>
|
||||
<textarea
|
||||
id={`additional-description-${index}`}
|
||||
name={`additional-description-${index}`}
|
||||
className="form-control shadow-sm custom-hover-border"
|
||||
rows="3"
|
||||
value={entry.description}
|
||||
onChange={(e) => handleDescriptionChange(index, e)}
|
||||
placeholder="Enter a description"
|
||||
required
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* Buttons */}
|
||||
<div className="d-flex justify-content-between mt-4">
|
||||
<button
|
||||
type="submit"
|
||||
className="btn btn-primary btn-lg shadow-sm"
|
||||
style={{
|
||||
background: 'linear-gradient(90deg, #0E6591, #0B4C6A)',
|
||||
border: 'none',
|
||||
color: '#fff',
|
||||
}}
|
||||
>
|
||||
Submit
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-outline btn-lg shadow-sm custom_manage_column_button"
|
||||
onClick={handleAddFields}
|
||||
>
|
||||
Add Entry
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
export default DataType6;
|
||||
387
src/components/Dashboard/DynamicForm/DynamicForm2.js
Normal file
387
src/components/Dashboard/DynamicForm/DynamicForm2.js
Normal file
@@ -0,0 +1,387 @@
|
||||
|
||||
|
||||
import React, { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import { useLocation, useNavigate } from "react-router-dom";
|
||||
import {
|
||||
Container,
|
||||
Row,
|
||||
Col,
|
||||
Table,
|
||||
Button,
|
||||
Dropdown,
|
||||
Form,
|
||||
Pagination,
|
||||
} from "react-bootstrap";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faTrash, faWrench, faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||
import Spinner from "../../../UIComponants/Spinner";
|
||||
import "../CSS/CSS/CommonStyle.css";
|
||||
import { BsJournals } from "react-icons/bs";
|
||||
import { fetchForms, deleteForm } from "../../../APIServices/DynamicFormAPI";
|
||||
import { toast } from "react-toastify";
|
||||
|
||||
const DynamicForm2 = () => {
|
||||
const [forms, setForms] = useState([]);
|
||||
const [error, setError] = useState(null);
|
||||
const [filteredForms, setFilteredForms] = useState([]);
|
||||
const [visibleColumns, setVisibleColumns] = useState({
|
||||
formName: true,
|
||||
formDescription: true,
|
||||
relatedTo: true,
|
||||
pageEvent: true,
|
||||
buttonCaption: true,
|
||||
goToForm: true,
|
||||
});
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [recordsPerPage, setRecordsPerPage] = useState(10);
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
}, 3000);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (location.state && location.state.formData) {
|
||||
setForms((prevForms) =>
|
||||
Array.isArray(prevForms)
|
||||
? [...prevForms, location.state.formData]
|
||||
: [location.state.formData]
|
||||
);
|
||||
} else {
|
||||
fetchForms();
|
||||
}
|
||||
}, [location.state]);
|
||||
|
||||
useEffect(() => {
|
||||
// Check if forms is an array before attempting to filter it
|
||||
if (Array.isArray(forms)) {
|
||||
const filtered = forms.filter((form) =>
|
||||
Object.values(form).some(
|
||||
(value) =>
|
||||
typeof value === "string" &&
|
||||
value.toLowerCase().includes(searchQuery.toLowerCase())
|
||||
)
|
||||
);
|
||||
setFilteredForms(filtered);
|
||||
}
|
||||
}, [searchQuery, forms]);
|
||||
|
||||
useEffect(() => {
|
||||
// loadForms();
|
||||
console.log("useEffect for loadForms triggered");
|
||||
if (location.state && location.state.formData) {
|
||||
console.log(
|
||||
"Adding form data from location state:",
|
||||
location.state.formData
|
||||
);
|
||||
setForms((prevForms) =>
|
||||
Array.isArray(prevForms)
|
||||
? [...prevForms, location.state.formData]
|
||||
: [location.state.formData]
|
||||
);
|
||||
}
|
||||
|
||||
console.log("Calling loadForms()");
|
||||
loadForms();
|
||||
}, [location.state]);
|
||||
|
||||
|
||||
const loadForms = async () => {
|
||||
console.log("loadForms function is called");
|
||||
try {
|
||||
const formData = await fetchForms();
|
||||
console.log("data fetching on form", formData);
|
||||
toast.success("Successfully fetched data!");
|
||||
setForms(formData);
|
||||
} catch (error) {
|
||||
setError("error fetching data ", error.message);
|
||||
toast.error("Error fetching data!");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = async (formId) => {
|
||||
console.log("Deleting data with ID:", formId);
|
||||
|
||||
if (!formId) {
|
||||
toast.error("Invalid ID. Unable to delete.");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// const apiUrl = `${process.env.REACT_APP_API_URL}/api/form_setup/${id}`;
|
||||
// const token = localStorage.getItem("authToken");
|
||||
// await axios.delete(apiUrl, {
|
||||
// headers: {
|
||||
// "Content-Type": "application/json",
|
||||
// Authorization: `Bearer ${token}`,
|
||||
// },
|
||||
// });
|
||||
const deleteFormData = await deleteForm(formId);
|
||||
toast.success("Successfully deleted data!");
|
||||
console.log("Deleted data:", deleteFormData);
|
||||
setForms((prevForms) => {
|
||||
return prevForms.filter((form) => {
|
||||
return form.id !== formId;
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error deleting data:", error);
|
||||
toast.error("Error deleting data!");
|
||||
}
|
||||
};
|
||||
|
||||
const handleBuild = async (id) => {
|
||||
try {
|
||||
const apiUrl = `${process.env.REACT_APP_API_URL}/api/dynamic_form_build`;
|
||||
const token = localStorage.getItem("authToken");
|
||||
await axios.post(
|
||||
apiUrl,
|
||||
{ id },
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Error building form:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
navigate("/admin/dynamic-form-add");
|
||||
};
|
||||
|
||||
const toggleColumn = (column) => {
|
||||
setVisibleColumns((prev) => ({ ...prev, [column]: !prev[column] }));
|
||||
};
|
||||
|
||||
const handleSearchChange = (event) => {
|
||||
setSearchQuery(event.target.value);
|
||||
};
|
||||
|
||||
const handleRecordsPerPageChange = (records) => {
|
||||
setRecordsPerPage(records);
|
||||
setCurrentPage(1); // Reset to the first page
|
||||
};
|
||||
|
||||
const handlePageChange = (page) => {
|
||||
setCurrentPage(page);
|
||||
};
|
||||
|
||||
const totalPages = Math.ceil(filteredForms.length / recordsPerPage);
|
||||
const currentRecords = filteredForms.slice(
|
||||
(currentPage - 1) * recordsPerPage,
|
||||
currentPage * recordsPerPage
|
||||
);
|
||||
|
||||
return (
|
||||
<div style={{ marginTop: "11rem" }}>
|
||||
{loading ? (
|
||||
<Spinner />
|
||||
) : (
|
||||
<Container
|
||||
fluid
|
||||
style={{ maxWidth: "95%", margin: "0 auto", overflowX: "hidden" }}
|
||||
>
|
||||
<Row>
|
||||
<Col>
|
||||
<h1 className="title_main">Dynamic Form</h1>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className="mb-3">
|
||||
<Col md={6}>
|
||||
<Form.Control
|
||||
type="text"
|
||||
placeholder="Search forms..."
|
||||
value={searchQuery}
|
||||
onChange={handleSearchChange}
|
||||
/>
|
||||
</Col>
|
||||
<Col md={6} className="text-end">
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={handleAdd}
|
||||
className="custom_button"
|
||||
>
|
||||
<FontAwesomeIcon icon={faPlus} /> Add
|
||||
</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col>
|
||||
<div className="table-wrapper">
|
||||
<Table
|
||||
striped
|
||||
hover
|
||||
responsive
|
||||
className="table-flush shadow-sm"
|
||||
align="middle"
|
||||
>
|
||||
<thead className="table-light custom_header">
|
||||
<tr>
|
||||
{visibleColumns.formName && <th>Form Name</th>}
|
||||
{visibleColumns.formDescription && (
|
||||
<th>Form Description</th>
|
||||
)}
|
||||
{visibleColumns.relatedTo && <th>Related To</th>}
|
||||
{visibleColumns.pageEvent && <th>Page Event</th>}
|
||||
{visibleColumns.buttonCaption && <th>Button Caption</th>}
|
||||
{visibleColumns.goToForm && <th>Go To Form</th>}
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{currentRecords.length > 0 ? (
|
||||
currentRecords.map((form, index) => (
|
||||
<tr key={index}>
|
||||
{visibleColumns.formName && <td>{form.formName}</td>}
|
||||
{visibleColumns.formDescription && (
|
||||
<td>{form.formDescription}</td>
|
||||
)}
|
||||
{visibleColumns.relatedTo && (
|
||||
<td>{form.relatedTo}</td>
|
||||
)}
|
||||
{visibleColumns.pageEvent && (
|
||||
<td>{form.pageEvent}</td>
|
||||
)}
|
||||
{visibleColumns.buttonCaption && (
|
||||
<td>{form.buttonCaption}</td>
|
||||
)}
|
||||
{visibleColumns.goToForm && <td>{form.goToForm}</td>}
|
||||
<td>
|
||||
<FontAwesomeIcon
|
||||
icon={faWrench}
|
||||
color="blue"
|
||||
style={{ cursor: "pointer" }}
|
||||
onClick={() => handleBuild(form.id)}
|
||||
/>
|
||||
<FontAwesomeIcon
|
||||
icon={faTrash}
|
||||
color="red"
|
||||
style={{ cursor: "pointer", marginLeft: "10px" }}
|
||||
onClick={() => {
|
||||
if (!form.id) {
|
||||
console.error("Invalid form ID:", form);
|
||||
toast.error("Cannot delete: Invalid ID!");
|
||||
return;
|
||||
}
|
||||
handleDelete(form.form_id);
|
||||
}}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
) : (
|
||||
<tr>
|
||||
<td colSpan="8" className="text-center text-muted">
|
||||
No forms available. Click "Add" to create a new one.
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</Table>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<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}
|
||||
checked={visibleColumns[column]}
|
||||
readOnly
|
||||
/>
|
||||
</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 justify-content-center mt-4">
|
||||
<Pagination.First
|
||||
disabled={currentPage === 1}
|
||||
onClick={() => handlePageChange(1)}
|
||||
/>
|
||||
<Pagination.Prev
|
||||
disabled={currentPage === 1}
|
||||
onClick={() => handlePageChange(currentPage - 1)}
|
||||
/>
|
||||
{[...Array(totalPages)].map((_, index) => (
|
||||
<Pagination.Item
|
||||
key={index + 1}
|
||||
active={currentPage === index + 1}
|
||||
onClick={() => handlePageChange(index + 1)}
|
||||
>
|
||||
{index + 1}
|
||||
</Pagination.Item>
|
||||
))}
|
||||
<Pagination.Next
|
||||
disabled={currentPage === totalPages}
|
||||
onClick={() => handlePageChange(currentPage + 1)}
|
||||
/>
|
||||
<Pagination.Last
|
||||
disabled={currentPage === totalPages}
|
||||
onClick={() => handlePageChange(totalPages)}
|
||||
/>
|
||||
</Pagination>
|
||||
</Container>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DynamicForm2;
|
||||
479
src/components/Dashboard/DynamicForm/DynamicFormAdd.js
Normal file
479
src/components/Dashboard/DynamicForm/DynamicFormAdd.js
Normal file
@@ -0,0 +1,479 @@
|
||||
// import React, { useState,useEffect } from 'react';
|
||||
// import { Box, Button, TextField, Select, MenuItem, FormControl, InputLabel, Checkbox, IconButton, Typography, FormControlLabel } from '@mui/material';
|
||||
// import DeleteIcon from '@mui/icons-material/Delete';
|
||||
// import { useNavigate } from 'react-router-dom';
|
||||
// import Spinner from '../../UIComponants/Spinner';
|
||||
|
||||
// function DynamicForm() {
|
||||
// const [loading, setLoading] = useState(true);
|
||||
// const [components, setComponents] = useState([{ label: '', type: '', mapping: '', readonly: false, values: '' }]);
|
||||
// const [formDetails, setFormDetails] = useState({
|
||||
// formName: '',
|
||||
// formDescription: '',
|
||||
// relatedTo: '',
|
||||
// pageEvent: '',
|
||||
// buttonName: '',
|
||||
// });
|
||||
|
||||
// useEffect(() => {
|
||||
// // Simulate loading data
|
||||
// setTimeout(() => {
|
||||
// setLoading(false);
|
||||
// }, 3000); // Simulated 3 seconds loading
|
||||
// }, []);
|
||||
|
||||
// const navigate = useNavigate();
|
||||
|
||||
// const handleFormChange = (e) => {
|
||||
// const { name, value } = e.target;
|
||||
// setFormDetails({ ...formDetails, [name]: value });
|
||||
// };
|
||||
|
||||
// const handleComponentChange = (index, field, value) => {
|
||||
// const updatedComponents = components.map((component, i) => (i === index ? { ...component, [field]: value } : component));
|
||||
// setComponents(updatedComponents);
|
||||
// };
|
||||
|
||||
// const addComponent = () => {
|
||||
// setComponents([...components, { label: '', type: '', mapping: '', readonly: false, values: '' }]);
|
||||
// };
|
||||
|
||||
// const removeComponent = (index) => {
|
||||
// setComponents(components.filter((_, i) => i !== index));
|
||||
// };
|
||||
|
||||
// const handleSubmit = (e) => {
|
||||
// e.preventDefault();
|
||||
// const formData = { ...formDetails, components };
|
||||
// navigate('/Dynamictable', { state: { formData } }); // Navigate to DynamicTable with formData
|
||||
// };
|
||||
|
||||
// return (
|
||||
// <div>
|
||||
// {loading ? (
|
||||
// <Spinner/>
|
||||
// ):(
|
||||
// <Box sx={{ padding: '20px' }}>
|
||||
// <Typography variant="h4" gutterBottom>Dynamic Form Setup</Typography>
|
||||
// <form onSubmit={handleSubmit}>
|
||||
// <Box sx={{ display: 'flex', gap: '20px', marginBottom: '20px' }}>
|
||||
// <TextField
|
||||
// label="Form Name"
|
||||
// name="formName"
|
||||
// value={formDetails.formName}
|
||||
// onChange={handleFormChange}
|
||||
// fullWidth
|
||||
// />
|
||||
// <TextField
|
||||
// label="Form Description"
|
||||
// name="formDescription"
|
||||
// value={formDetails.formDescription}
|
||||
// onChange={handleFormChange}
|
||||
// fullWidth
|
||||
// />
|
||||
// <FormControl fullWidth>
|
||||
// <InputLabel>Related To</InputLabel>
|
||||
// <Select
|
||||
// name="relatedTo"
|
||||
// value={formDetails.relatedTo}
|
||||
// onChange={handleFormChange}
|
||||
// >
|
||||
// <MenuItem value=""><em>None</em></MenuItem>
|
||||
// <MenuItem value="Menu">Menu</MenuItem>
|
||||
// <MenuItem value="Related to">Related to</MenuItem>
|
||||
// </Select>
|
||||
// </FormControl>
|
||||
// </Box>
|
||||
// <Box sx={{ display: 'flex', gap: '20px', marginBottom: '20px' }}>
|
||||
// <FormControl fullWidth>
|
||||
// <InputLabel>Page Event</InputLabel>
|
||||
// <Select
|
||||
// name="pageEvent"
|
||||
// value={formDetails.pageEvent}
|
||||
// onChange={handleFormChange}
|
||||
// >
|
||||
// <MenuItem value="Onclick">Onclick</MenuItem>
|
||||
// <MenuItem value="Onblur">Onblur</MenuItem>
|
||||
// </Select>
|
||||
// </FormControl>
|
||||
// <TextField
|
||||
// label="Button Name"
|
||||
// name="buttonName"
|
||||
// value={formDetails.buttonName}
|
||||
// onChange={handleFormChange}
|
||||
// fullWidth
|
||||
// />
|
||||
// </Box>
|
||||
// <Typography variant="h6" gutterBottom>Component Details</Typography>
|
||||
// {components.map((component, index) => (
|
||||
// <Box key={index} sx={{ display: 'flex', gap: '10px', alignItems: 'center', marginBottom: '10px' }}>
|
||||
// <TextField
|
||||
// label="Label"
|
||||
// value={component.label}
|
||||
// onChange={(e) => handleComponentChange(index, 'label', e.target.value)}
|
||||
// fullWidth
|
||||
// />
|
||||
// <FormControl fullWidth>
|
||||
// <InputLabel>Type</InputLabel>
|
||||
// <Select
|
||||
// value={component.type}
|
||||
// onChange={(e) => handleComponentChange(index, 'type', e.target.value)}
|
||||
// >
|
||||
// <MenuItem value=""><em>None</em></MenuItem>
|
||||
// <MenuItem value="textfield">TextField</MenuItem>
|
||||
// <MenuItem value="checkbox">Checkbox</MenuItem>
|
||||
// <MenuItem value="select">Select</MenuItem>
|
||||
// </Select>
|
||||
// </FormControl>
|
||||
// <TextField
|
||||
// label="Mapping"
|
||||
// value={component.mapping}
|
||||
// onChange={(e) => handleComponentChange(index, 'mapping', e.target.value)}
|
||||
// fullWidth
|
||||
// />
|
||||
// <FormControlLabel
|
||||
// control={
|
||||
// <Checkbox
|
||||
// checked={component.readonly}
|
||||
// onChange={(e) => handleComponentChange(index, 'readonly', e.target.checked)}
|
||||
// />
|
||||
// }
|
||||
// label="Readonly"
|
||||
// />
|
||||
// <TextField
|
||||
// label="Enter Values"
|
||||
// value={component.values}
|
||||
// onChange={(e) => handleComponentChange(index, 'values', e.target.value)}
|
||||
// fullWidth
|
||||
// />
|
||||
// <IconButton onClick={() => removeComponent(index)}>
|
||||
// <DeleteIcon />
|
||||
// </IconButton>
|
||||
// </Box>
|
||||
// ))}
|
||||
// <Button onClick={addComponent} variant="contained" sx={{ marginBottom: '20px' }}>Add Component</Button>
|
||||
// <Button type="submit" variant="contained" color="primary">Submit</Button>
|
||||
// </form>
|
||||
// </Box>
|
||||
// )}
|
||||
// </div>
|
||||
|
||||
// );
|
||||
// }
|
||||
|
||||
// export default DynamicForm;
|
||||
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faTrash } from "@fortawesome/free-solid-svg-icons";
|
||||
import {
|
||||
Button,
|
||||
Form,
|
||||
FormGroup,
|
||||
Label,
|
||||
Input,
|
||||
FormText,
|
||||
Row,
|
||||
Col,
|
||||
Card,
|
||||
CardBody,
|
||||
CardTitle,
|
||||
} from "reactstrap";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import Spinner from "UIComponants/Spinner";
|
||||
import { buildForm } from '../../../APIServices/DynamicFormAPI';
|
||||
import { toast } from "react-toastify";
|
||||
|
||||
|
||||
function DynamicFormAdd() {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [components, setComponents] = useState([
|
||||
{ label: "", type: "", mapping: "", readonly: false, values: "" },
|
||||
]);
|
||||
const [formDetails, setFormDetails] = useState({
|
||||
formName: "",
|
||||
formDescription: "",
|
||||
relatedTo: "",
|
||||
pageEvent: "",
|
||||
buttonName: "",
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
}, 3000); // Simulated 3 seconds loading
|
||||
}, []);
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleFormChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
setFormDetails({ ...formDetails, [name]: value });
|
||||
};
|
||||
|
||||
const handleComponentChange = (index, field, value) => {
|
||||
const updatedComponents = components.map((component, i) =>
|
||||
i === index ? { ...component, [field]: value } : component
|
||||
);
|
||||
setComponents(updatedComponents);
|
||||
};
|
||||
|
||||
const addComponent = () => {
|
||||
setComponents([
|
||||
...components,
|
||||
{ label: "", type: "", mapping: "", readonly: false, values: "" },
|
||||
]);
|
||||
};
|
||||
|
||||
const removeComponent = (index) => {
|
||||
setComponents(components.filter((_, i) => i !== index));
|
||||
};
|
||||
|
||||
// const handleSubmit = async (id) => {
|
||||
// try {
|
||||
// const {status} = await buildForm(id);
|
||||
// if(status >= 200 && status <= 209){
|
||||
// console.log("Form built successfully");
|
||||
// toast.success('Form built successfully');
|
||||
// const formData = { ...formDetails, components };
|
||||
// navigate("/admin/dynamic-form", { state: { formData } });
|
||||
// }else{
|
||||
// toast.error('Error building form');
|
||||
// console.log("Error building form");
|
||||
// }
|
||||
// } catch (error) {
|
||||
|
||||
// console.error("Error saving form:", error);
|
||||
// toast.error(error || 'Error saving form');
|
||||
// }
|
||||
// };
|
||||
|
||||
// const handleSubmit = async (id) => {
|
||||
// try {
|
||||
// console.log("Form data to be submitted:", formDetails, components);
|
||||
// const { status } = await buildForm(id); // Ensure `id` is defined
|
||||
// if (status >= 200 && status <= 209) {
|
||||
// console.log("Form built successfully");
|
||||
// toast.success("Form built successfully");
|
||||
// const formData = { ...formDetails, components };
|
||||
// navigate("/admin/dynamic-form", { state: { formData } });
|
||||
// } else {
|
||||
// toast.error("Error building form");
|
||||
// console.log("Error response status:", status);
|
||||
// }
|
||||
// } catch (error) {
|
||||
// console.error("Error submitting form:", error);
|
||||
// toast.error("Error submitting form");
|
||||
// }
|
||||
// };
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
try {
|
||||
// Combine formDetails and components for the API payload
|
||||
const formData = {
|
||||
...formDetails,
|
||||
components,
|
||||
};
|
||||
|
||||
console.log("Form data to be submitted:", formData);
|
||||
|
||||
const { status } = await buildForm(formData); // Pass formData to the API call
|
||||
if (status >= 200 && status <= 209) {
|
||||
toast.success("Form built successfully");
|
||||
navigate("/admin/dynamic-form", { state: { formData } });
|
||||
} else {
|
||||
toast.error("Error building form");
|
||||
console.log("Error response status:", status);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error submitting form:", error);
|
||||
toast.error("Error submitting form");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div style={{ marginTop: "11rem" }}>
|
||||
{loading ? (
|
||||
<Spinner/>
|
||||
) : (
|
||||
<Card className="p-4 mb-7">
|
||||
<CardBody>
|
||||
<CardTitle tag="h4">Dynamic Form Setup</CardTitle>
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<Row className="mb-3">
|
||||
<Col md={4}>
|
||||
<FormGroup>
|
||||
<Label for="formName">Form Name</Label>
|
||||
<Input
|
||||
type="text"
|
||||
id="formName"
|
||||
name="formName"
|
||||
value={formDetails.formName}
|
||||
onChange={handleFormChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
<Col md={4}>
|
||||
<FormGroup>
|
||||
<Label for="formDescription">Form Description</Label>
|
||||
<Input
|
||||
type="text"
|
||||
id="formDescription"
|
||||
name="formDescription"
|
||||
value={formDetails.formDescription}
|
||||
onChange={handleFormChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
<Col md={4}>
|
||||
<FormGroup>
|
||||
<Label for="relatedTo">Related To</Label>
|
||||
<Input
|
||||
type="select"
|
||||
id="relatedTo"
|
||||
name="relatedTo"
|
||||
value={formDetails.relatedTo}
|
||||
onChange={handleFormChange}
|
||||
>
|
||||
<option value="">Select</option>
|
||||
<option value="Menu">Menu</option>
|
||||
<option value="Related to">Related to</option>
|
||||
</Input>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className="mb-3">
|
||||
<Col md={6}>
|
||||
<FormGroup>
|
||||
<Label for="pageEvent">Page Event</Label>
|
||||
<Input
|
||||
type="select"
|
||||
id="pageEvent"
|
||||
name="pageEvent"
|
||||
value={formDetails.pageEvent}
|
||||
onChange={handleFormChange}
|
||||
>
|
||||
<option value="Onclick">Onclick</option>
|
||||
<option value="Onblur">Onblur</option>
|
||||
</Input>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
<Col md={6}>
|
||||
<FormGroup>
|
||||
<Label for="buttonName">Button Name</Label>
|
||||
<Input
|
||||
type="text"
|
||||
id="buttonName"
|
||||
name="buttonName"
|
||||
value={formDetails.buttonName}
|
||||
onChange={handleFormChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
<h5>Component Details</h5>
|
||||
{components.map((component, index) => (
|
||||
<Row key={index} className="align-items-center mb-3">
|
||||
<Col md={2}>
|
||||
<FormGroup>
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="Label"
|
||||
value={component.label}
|
||||
onChange={(e) =>
|
||||
handleComponentChange(index, "label", e.target.value)
|
||||
}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
<Col md={2}>
|
||||
<FormGroup>
|
||||
<Input
|
||||
type="select"
|
||||
value={component.type}
|
||||
onChange={(e) =>
|
||||
handleComponentChange(index, "type", e.target.value)
|
||||
}
|
||||
>
|
||||
<option value="">Type</option>
|
||||
<option value="textfield">TextField</option>
|
||||
<option value="checkbox">Checkbox</option>
|
||||
<option value="select">Select</option>
|
||||
</Input>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
<Col md={2}>
|
||||
<FormGroup>
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="Mapping"
|
||||
value={component.mapping}
|
||||
onChange={(e) =>
|
||||
handleComponentChange(
|
||||
index,
|
||||
"mapping",
|
||||
e.target.value
|
||||
)
|
||||
}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
<Col md={2}>
|
||||
<FormGroup check>
|
||||
<Label check>
|
||||
<Input
|
||||
type="checkbox"
|
||||
checked={component.readonly}
|
||||
onChange={(e) =>
|
||||
handleComponentChange(
|
||||
index,
|
||||
"readonly",
|
||||
e.target.checked
|
||||
)
|
||||
}
|
||||
/>{" "}
|
||||
Readonly
|
||||
</Label>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
<Col md={2}>
|
||||
<FormGroup>
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="Values"
|
||||
value={component.values}
|
||||
onChange={(e) =>
|
||||
handleComponentChange(index, "values", e.target.value)
|
||||
}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
<Col md={2}>
|
||||
<FontAwesomeIcon
|
||||
icon={faTrash}
|
||||
className="text-danger cursor-pointer"
|
||||
onClick={() => removeComponent(index)}
|
||||
title="Remove"
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
))}
|
||||
<Button color="primary" onClick={addComponent} className="me-3">
|
||||
Add Component
|
||||
</Button>
|
||||
<Button color="success" type="submit" onClick={handleSubmit}>
|
||||
Submit
|
||||
</Button>
|
||||
</Form>
|
||||
</CardBody>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default DynamicFormAdd;
|
||||
9
src/components/Dashboard/MappingRule.js
Normal file
9
src/components/Dashboard/MappingRule.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import React from 'react'
|
||||
|
||||
const MappingRule = () => {
|
||||
return (
|
||||
<div>MappingRule</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default MappingRule
|
||||
723
src/components/Dashboard/MenuAccessControl.js
Normal file
723
src/components/Dashboard/MenuAccessControl.js
Normal file
@@ -0,0 +1,723 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Button, Dropdown, Modal, Form,Row,Col,InputGroup,
|
||||
FormControl,
|
||||
} from "react-bootstrap";
|
||||
import { Table ,Pagination,
|
||||
PaginationItem,
|
||||
PaginationLink,Input,Label} from 'reactstrap';
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faEdit, faTrashAlt, faPlus, faBars,
|
||||
faTimes } from "@fortawesome/free-solid-svg-icons";
|
||||
import "bootstrap/dist/css/bootstrap.min.css";
|
||||
import "../Dashboard/CSS/CSS/CommonStyle.css";
|
||||
import { FaSearch, FaTimes } from "react-icons/fa";
|
||||
import { BsJournals } from "react-icons/bs";
|
||||
import Spinner from '../../UIComponants/Spinner';
|
||||
import { getByUsrGrpId } from "APIServices/MenuAccessControlAPI";
|
||||
import { toast } from "react-toastify";
|
||||
|
||||
function MenuAccessControl({ selected,Sync}) {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [menuItems, setMenuItems] = useState([]);
|
||||
const [menus,setMenus] = useState([]);
|
||||
const [showAddEditModal, setShowAddEditModal] = useState(false);
|
||||
const [selectedMenuId, setSelectedMenuId] = useState(null);
|
||||
const [usrgrp, setUsrgrp] = useState('DefaultGroup');
|
||||
const [allData, setAllData] = useState([]);
|
||||
|
||||
|
||||
const [currentMenuItem, setCurrentMenuItem] = useState({
|
||||
menuId: "",
|
||||
menuItemName: "",
|
||||
accessLevel: "",
|
||||
isActive: false,
|
||||
view: false, // For View checkbox
|
||||
create: false, // For Create checkbox
|
||||
edit: false, // For Edit checkbox
|
||||
delete: false, // For Delete checkbox
|
||||
query: false, // For Query checkbox
|
||||
export: false, // For Export checkbox
|
||||
});
|
||||
const [alldata, setAlldata] = useState([]);
|
||||
// const [slicedMenus, setSlicedMenus] = useState([]);
|
||||
const [toggle, setToggle] = useState(false);
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const [recordsPerPage, setRecordsPerPage] = useState(10);
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
let [slicedMenus, setSlicedMenus] = useState([
|
||||
{
|
||||
menuId: 1,
|
||||
view: false,
|
||||
create: false,
|
||||
edit: false,
|
||||
delete: false,
|
||||
query: false,
|
||||
export: false,
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
menuId: 2,
|
||||
view: false,
|
||||
create: false,
|
||||
edit: false,
|
||||
delete: false,
|
||||
query: false,
|
||||
export: false,
|
||||
isActive: false,
|
||||
},
|
||||
]);
|
||||
const [visibleColumns, setVisibleColumns] = useState({
|
||||
No:true,
|
||||
menuId: true,
|
||||
menuItemName: true,
|
||||
view:true,
|
||||
create:true,
|
||||
edit:true,
|
||||
delete:true,
|
||||
query:true,
|
||||
export:true,
|
||||
isActive: true,
|
||||
actions: true
|
||||
});
|
||||
|
||||
const [newItemData, setNewItemData] = useState({
|
||||
|
||||
menuId: "",
|
||||
menuItemName: "",
|
||||
view:"",
|
||||
create:"",
|
||||
edit:"",
|
||||
delete:"",
|
||||
query:"",
|
||||
export:"",
|
||||
isActive: true,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate loading data
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
}, 3000); // Simulated 3 seconds loading
|
||||
}, []);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate fetching or setting a default value
|
||||
// const defaultGroup = 'Admin'; // Replace with fetched or derived value
|
||||
setUsrgrp();
|
||||
}, []);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const apiUrl = `${process.env.REACT_APP_API_URL}/api/getAllMenuItems`;
|
||||
const token = localStorage.getItem("authToken");
|
||||
const fetchMenuItems = 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();
|
||||
setMenuItems(data);
|
||||
} catch (error) {
|
||||
console.error("Error fetching menu items:", error);
|
||||
}
|
||||
};
|
||||
|
||||
fetchMenuItems();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
// const fetchData = async () => {
|
||||
// try {
|
||||
// const data = await getByUsrGrpId(usrgrp);
|
||||
// console.log("Data fetched successfully:", data);
|
||||
|
||||
// setAlldata(data); // Update the state with fetched data
|
||||
// toast.success("Data fetched successfully!");
|
||||
// } catch (error) {
|
||||
// console.error('Error fetching data:', error);
|
||||
// toast.error("Failed to fetch data.");
|
||||
// }
|
||||
// };
|
||||
|
||||
// const fetchData = async () => {
|
||||
// try {
|
||||
// console.log("Fetching data for usrgrp:", usrgrp);
|
||||
// const data = await getByUsrGrpId(usrgrp);
|
||||
// if (!data) {
|
||||
// console.error("Received undefined data");
|
||||
// toast.error("Failed to fetch data.");
|
||||
// return;
|
||||
// }
|
||||
// console.log("Data fetched successfully:", data);
|
||||
// setAlldata(data);
|
||||
// toast.success("Data fetched successfully!");
|
||||
// } catch (error) {
|
||||
// console.error("Error fetching data:", error);
|
||||
// toast.error("Failed to fetch data.");
|
||||
// }
|
||||
// };
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
console.log("Fetching data for usrgrp:", usrgrp);
|
||||
const data = await getByUsrGrpId(usrgrp);
|
||||
if (!data) {
|
||||
console.error("Received undefined data");
|
||||
toast.error("Failed to fetch data.");
|
||||
return;
|
||||
}
|
||||
console.log("Data fetched successfully:", data);
|
||||
setAlldata(data);
|
||||
toast.success("Data fetched successfully!");
|
||||
} catch (error) {
|
||||
console.error("Error fetching data:", error.message || error);
|
||||
toast.error("Failed to fetch data.");
|
||||
}
|
||||
};
|
||||
const idselected = (value) => {
|
||||
setSelectedMenuId(value);
|
||||
};
|
||||
|
||||
const toggleColumn = (column) => {
|
||||
setVisibleColumns((prev) => ({
|
||||
...prev,
|
||||
[column]: !prev[column],
|
||||
}));
|
||||
};
|
||||
|
||||
const handleInputChange = (event,menuId) => {
|
||||
const { name, value, checked, type } = event.target;
|
||||
setCurrentMenuItem(prev => ({
|
||||
...prev,
|
||||
[name]: type === "checkbox" ? checked : value
|
||||
}));
|
||||
setSlicedMenus((prevMenus) =>
|
||||
prevMenus.map((menu) =>
|
||||
menu.menuId === currentMenuItem.menuId // Identify the correct menu item
|
||||
? { ...menu, [name]: value } // Update the specific field
|
||||
: menu
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const handleSearch = (query) => {
|
||||
setSearchQuery(query);
|
||||
};
|
||||
|
||||
const handleAddItem = () => {
|
||||
setIsEditing(false);
|
||||
setNewItemData({
|
||||
menuId: "",
|
||||
menuItemName: "",
|
||||
isActive: true,
|
||||
});
|
||||
setShowAddEditModal(true)
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setShowAddEditModal(false); // Close the modal by setting the state to false
|
||||
};
|
||||
|
||||
|
||||
const handleSubmit = (event) => {
|
||||
event.preventDefault();
|
||||
if (isEditing) {
|
||||
setMenuItems(menuItems.map(item =>
|
||||
item.menuId === currentMenuItem.menuId ? currentMenuItem : item
|
||||
));
|
||||
} else {
|
||||
const newMenuId = `ID${menuItems.length + 1}`;
|
||||
setMenuItems([...menuItems, { ...currentMenuItem, menuId: newMenuId }]);
|
||||
}
|
||||
setShowAddEditModal(false);
|
||||
};
|
||||
|
||||
const openModal = (item = { menuId: "", menuName: "", accessLevel: "", isActive: false }) => {
|
||||
setIsEditing(!!item.menuId);
|
||||
setCurrentMenuItem(item);
|
||||
setShowAddEditModal(true);
|
||||
};
|
||||
|
||||
const handleDelete = (menuId) => {
|
||||
setMenuItems(menuItems.filter(item => item.menuId !== menuId));
|
||||
};
|
||||
|
||||
const handleRecordsPerPageChange=(number) =>{
|
||||
setRecordsPerPage(number);
|
||||
setCurrentPage(1);
|
||||
}
|
||||
|
||||
const totalPages = Math.ceil(menuItems.length / recordsPerPage);
|
||||
const handlePageChange = (pageNumber) => {
|
||||
setCurrentPage(pageNumber);
|
||||
};
|
||||
|
||||
const handleSelectChange = (value) => {
|
||||
console.log(value);
|
||||
setUsrgrp(value);
|
||||
|
||||
};
|
||||
|
||||
const handleToggleCheckbox = () => {
|
||||
setToggle(!toggle);
|
||||
};
|
||||
|
||||
slicedMenus = menuItems
|
||||
.filter((item) =>
|
||||
item.menuName && item.menuName.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"
|
||||
style={{ gap: "15px" }}
|
||||
>
|
||||
{/* Left Side */}
|
||||
<div className="d-flex align-items-center" style={{ gap: "15px" }}>
|
||||
<h1 className="title_main mb-0">Menu Access Control</h1>
|
||||
<span
|
||||
className="badge bg-info text-white"
|
||||
style={{ fontSize: "14px", padding: "5px 10px", lineHeight: "1" }}
|
||||
>
|
||||
Edit Mode
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Right Side */}
|
||||
<div className="d-flex align-items-center" style={{ gap: "15px" }}>
|
||||
<span>For</span>
|
||||
<select
|
||||
className="form-select"
|
||||
style={{ width: "150px", height: "40px" }}
|
||||
onChange={(e) => handleSelectChange(e.target.value)}
|
||||
>
|
||||
{alldata.map((sub) => (
|
||||
<option
|
||||
key={sub.usrGrp}
|
||||
value={sub.usrGrp}
|
||||
selected={sub.usrGrp === 40}
|
||||
>
|
||||
{sub.groupName}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<Button
|
||||
variant="primary"
|
||||
className="ml-3"
|
||||
style={{ height: "40px", lineHeight: "1.2" }}
|
||||
onClick={() => fetchData()}
|
||||
>
|
||||
Reload
|
||||
</Button>
|
||||
<span style={{ fontWeight: "500" }}>
|
||||
{toggle ? "Only Main Menu" : "Show All"}
|
||||
</span>
|
||||
|
||||
<div className="form-check form-switch">
|
||||
<input
|
||||
className="form-check-input"
|
||||
type="checkbox"
|
||||
id="toggleSwitch"
|
||||
checked={toggle}
|
||||
onChange={handleToggleCheckbox}
|
||||
/>
|
||||
<label className="form-check-label" htmlFor="toggleSwitch">
|
||||
Toggle
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</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">
|
||||
<>
|
||||
{/* Add Icon */}
|
||||
<FontAwesomeIcon
|
||||
icon={faPlus}
|
||||
onClick={() => handleAddItem(true)}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
fontSize: "1.5rem",
|
||||
color: "#747264",
|
||||
marginRight: "20px",
|
||||
}}
|
||||
/>
|
||||
<FontAwesomeIcon
|
||||
icon={faBars}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
fontSize: "1.5rem",
|
||||
color: "#747264",
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<div className="table-responsive">
|
||||
<Table striped responsive hover align="middle" className=" table-flush shadow-sm">
|
||||
<thead className="custom_header">
|
||||
<tr>
|
||||
{Object.entries(visibleColumns).map(([key, visible]) =>
|
||||
visible ? <th key={key}>{key.charAt(0).toUpperCase() + key.slice(1)}</th> : null
|
||||
)}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="tbody">
|
||||
{slicedMenus.length === 0 ? (
|
||||
<tr>
|
||||
<td
|
||||
colSpan={
|
||||
Object.keys(visibleColumns).filter((key) => visibleColumns[key])
|
||||
.length
|
||||
}
|
||||
className="text-center"
|
||||
>
|
||||
No items found. Please add new items.
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
slicedMenus.map((item, index) => (
|
||||
<tr key={index}>
|
||||
{Object.entries(visibleColumns).map(([key, visible]) =>
|
||||
visible ? (
|
||||
<td key={key}>
|
||||
{key === "actions" ? (
|
||||
<>
|
||||
<FontAwesomeIcon
|
||||
icon={faEdit}
|
||||
onClick={() => openModal(item)}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
fontSize: "1rem",
|
||||
color: "green",
|
||||
marginRight: "15px",
|
||||
}}
|
||||
/>
|
||||
<FontAwesomeIcon
|
||||
icon={faTrashAlt}
|
||||
onClick={() => handleDelete(item.menuId)}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
fontSize: "1rem",
|
||||
color: "#dc3545",
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
) :["view", "create", "edit", "delete", "query", "export"].includes(key) ? (
|
||||
<Input
|
||||
type="checkbox"
|
||||
checked={item[key]}
|
||||
// onChange={(e) =>
|
||||
// handleInputChange({
|
||||
// target: {
|
||||
// name: key,
|
||||
// value: e.target.checked,
|
||||
// },
|
||||
// })
|
||||
onChange={(e) => handleInputChange(e, item.menuId)}
|
||||
disabled={false}
|
||||
/>
|
||||
): key === "isActive" ? (
|
||||
<span
|
||||
className="status"
|
||||
style={{
|
||||
color: item.isActive ? "green" : "red",
|
||||
backgroundColor: item.isActive
|
||||
? "#d4f7d4"
|
||||
: "#f7d4d4",
|
||||
padding: "4px 8px",
|
||||
borderRadius: "4px",
|
||||
display: "inline-block",
|
||||
}}
|
||||
>
|
||||
{item.isActive ? "Active" : "Inactive"}
|
||||
</span>
|
||||
) : (
|
||||
item[key]
|
||||
)}
|
||||
</td>
|
||||
) : null
|
||||
)}
|
||||
</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={showAddEditModal} onHide={() => setShowAddEditModal(false)}>
|
||||
<Modal.Header >
|
||||
<Modal.Title>{isEditing ? "Edit Menu Access" : "Add Menu Access"}</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="formMenuName">
|
||||
<Form.Label>Menu Name</Form.Label>
|
||||
<Form.Control
|
||||
type="text"
|
||||
name="menuName"
|
||||
value={currentMenuItem.menuName}
|
||||
onChange={handleInputChange}
|
||||
required
|
||||
className="custom-hover-border"
|
||||
/>
|
||||
</Form.Group>
|
||||
<Form.Group>
|
||||
<Label for="name">Menu Name<span className="required-field">*</span></Label>
|
||||
<Input type="select" style={{ width: '100%', padding: '5px', border: '1px solid #ccc', borderRadius: '4px' }} onChange={(e) => idselected(e.target.value)}>
|
||||
{menus.map((sub) => (
|
||||
<option key={sub.menuItemId} value={sub.menuItemId}>{sub.menuItemDesc}</option>
|
||||
))}
|
||||
</Input>
|
||||
</Form.Group>
|
||||
<Form.Group controlId="formActive">
|
||||
<Form.Check
|
||||
type="checkbox"
|
||||
label="Active?"
|
||||
name="isActive"
|
||||
checked={currentMenuItem.isActive}
|
||||
onChange={handleInputChange}
|
||||
className="custom-checkbox"
|
||||
/>
|
||||
</Form.Group>
|
||||
<Modal.Footer>
|
||||
|
||||
<Button variant="primary" type="submit" className="custom_button px-4">{isEditing ? "Update" : "Add"}</Button>
|
||||
<Button variant="secondary" onClick={handleClose} className="custom_button px-4">
|
||||
Cancel
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Form>
|
||||
</Modal.Body>
|
||||
</Modal> */}
|
||||
|
||||
<Modal show={showAddEditModal} onHide={() => setShowAddEditModal(false)}>
|
||||
<Modal.Header>
|
||||
<Modal.Title>{isEditing ? "Edit Menu Access" : "Add Menu Access"}</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}>
|
||||
{/* Menu Name Input */}
|
||||
<Form.Group controlId="formMenuName">
|
||||
<Form.Label>Menu Items Name</Form.Label>
|
||||
<Form.Control
|
||||
type="text"
|
||||
name="menuName"
|
||||
value={currentMenuItem.menuName}
|
||||
onChange={handleInputChange}
|
||||
required
|
||||
className="custom-hover-border"
|
||||
/>
|
||||
</Form.Group>
|
||||
|
||||
{/* Select Menu Item */}
|
||||
<Form.Group>
|
||||
<Label for="name">Menu Item<span className="required-field">*</span></Label>
|
||||
<Input
|
||||
type="select"
|
||||
style={{ width: '100%', padding: '5px', border: '1px solid #ccc', borderRadius: '4px' }}
|
||||
onChange={(e) => idselected(e.target.value)}
|
||||
>
|
||||
{menus.map((sub) => (
|
||||
<option key={sub.menuItemId} value={sub.menuItemId}>
|
||||
{sub.menuItemDesc}
|
||||
</option>
|
||||
))}
|
||||
</Input>
|
||||
</Form.Group>
|
||||
|
||||
{/* Active Checkbox */}
|
||||
<Form.Group controlId="formActive">
|
||||
<Form.Check
|
||||
type="checkbox"
|
||||
label="Active?"
|
||||
name="isActive"
|
||||
checked={currentMenuItem.isActive}
|
||||
onChange={handleInputChange}
|
||||
className="custom-checkbox"
|
||||
/>
|
||||
</Form.Group>
|
||||
|
||||
{/* Modal Footer with buttons */}
|
||||
<Modal.Footer>
|
||||
<Button variant="primary" type="submit" className="custom_button px-4">
|
||||
{isEditing ? "Update" : "Add"}
|
||||
</Button>
|
||||
<Button variant="secondary" onClick={handleClose} className="custom_button px-4">
|
||||
Cancel
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Form>
|
||||
</Modal.Body>
|
||||
</Modal>
|
||||
|
||||
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default MenuAccessControl;
|
||||
1627
src/components/Dashboard/MenuAccessControl2.js
Normal file
1627
src/components/Dashboard/MenuAccessControl2.js
Normal file
File diff suppressed because it is too large
Load Diff
1048
src/components/Dashboard/MenuMaintenance.js
Normal file
1048
src/components/Dashboard/MenuMaintenance.js
Normal file
File diff suppressed because it is too large
Load Diff
9
src/components/Dashboard/MultiDynamicsBugs.js
Normal file
9
src/components/Dashboard/MultiDynamicsBugs.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import React from 'react'
|
||||
|
||||
const MultiDynamicsBugs = () => {
|
||||
return (
|
||||
<div>MultiDynamicsBugs</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default MultiDynamicsBugs
|
||||
0
src/components/Dashboard/ProfileDropdown.js
Normal file
0
src/components/Dashboard/ProfileDropdown.js
Normal file
936
src/components/Dashboard/ReportRunner/ReportRunner2Edit.js
Normal file
936
src/components/Dashboard/ReportRunner/ReportRunner2Edit.js
Normal file
@@ -0,0 +1,936 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { fetchStandardParameters, downloadFile } from "../../../APIServices/ReportRunnerAPI";
|
||||
import ReportBuilderService from "../../../APIServices/ReportBuilderService";
|
||||
import { toast } from "react-toastify";
|
||||
import './ReportrunnerEdit.css'
|
||||
import { saveAs } from 'file-saver';
|
||||
|
||||
import axios from 'axios';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
|
||||
const ReportRunner2Edit = () => {
|
||||
const navigate = useNavigate();
|
||||
const { id } = useParams();
|
||||
|
||||
// Report data state
|
||||
const [reportName, setReportName] = useState("Sample Report");
|
||||
const [reportId, setReportId] = useState(id || null);
|
||||
|
||||
// Date parameters state
|
||||
const [dateParam, setDateParam] = useState(true);
|
||||
const [selectDateType, setSelectDateType] = useState("");
|
||||
const [fromDate, setFromDate] = useState("");
|
||||
const [toDate, setToDate] = useState("");
|
||||
const [fromDateQuery, setFromDateQuery] = useState("");
|
||||
const [toDateQuery, setToDateQuery] = useState("");
|
||||
|
||||
// Standard parameters state
|
||||
const [dynamicFields, setDynamicFields] = useState([]);
|
||||
const [dynamicFormValues, setDynamicFormValues] = useState({});
|
||||
|
||||
// Adhoc parameters state
|
||||
const [adhocList, setAdhocList] = useState([]);
|
||||
const [adhocParams, setAdhocParams] = useState([
|
||||
{ andor: "AND", fields_name: "", condition: "=", value: "" },
|
||||
]);
|
||||
|
||||
// Condition options
|
||||
const conditionOptions = ["=", "!=", "<", ">", "<=", ">=", "LIKE", "BETWEEN", "IN"];
|
||||
const andOrOptions = ["AND", "OR", "NOT"];
|
||||
|
||||
// Results state
|
||||
const [rows, setRows] = useState([]);
|
||||
const [filterRows, setFilterRows] = useState([]);
|
||||
const [filtered, setFiltered] = useState(false);
|
||||
const [headers, setHeaders] = useState([]);
|
||||
|
||||
// Query state
|
||||
const [SQLQuery, setSQLQuery] = useState('');
|
||||
const [dateKey, setDateKey] = useState('createdat');
|
||||
const [formattedAdhocParameters, setFormattedAdhocParameters] = useState('');
|
||||
const [selectedValues, setSelectedValues] = useState({});
|
||||
|
||||
// Loading state
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
|
||||
// Get today's date
|
||||
const todayDate = new Date().toISOString().slice(0, 10);
|
||||
|
||||
// Add this at the beginning of the component with other state variables
|
||||
const [showExportDropdown, setShowExportDropdown] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
// Fetch report details
|
||||
if (reportId) {
|
||||
fetchReportDetails(reportId);
|
||||
}
|
||||
|
||||
// Set default date to today
|
||||
handleDateSelect('Today');
|
||||
|
||||
// Run query after a delay to allow data to load
|
||||
const timer = setTimeout(() => {
|
||||
runQuery();
|
||||
}, 2000);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, [reportId]);
|
||||
|
||||
useEffect(() => {
|
||||
// Function to handle clicks outside the dropdown
|
||||
const handleClickOutside = (event) => {
|
||||
// Get reference to the dropdown container
|
||||
const dropdownContainer = document.getElementById('export-dropdown-container');
|
||||
|
||||
// Close dropdown if click is outside the container
|
||||
if (dropdownContainer && !dropdownContainer.contains(event.target)) {
|
||||
setShowExportDropdown(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Function to handle escape key press
|
||||
const handleEscapeKey = (event) => {
|
||||
if (event.key === 'Escape') {
|
||||
setShowExportDropdown(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Add event listeners if dropdown is open
|
||||
if (showExportDropdown) {
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
document.addEventListener('keydown', handleEscapeKey);
|
||||
}
|
||||
|
||||
// Clean up event listeners when dropdown closes or component unmounts
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', handleClickOutside);
|
||||
document.removeEventListener('keydown', handleEscapeKey);
|
||||
};
|
||||
}, [showExportDropdown]);
|
||||
|
||||
const fetchReportDetails = async (id) => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
// Fetch report details
|
||||
const response = await ReportBuilderService.getrbDetailsById(id);
|
||||
const data = response?.data;
|
||||
if (!data) {
|
||||
throw new Error("No data received from the API");
|
||||
}
|
||||
console.log("Report details received:", data);
|
||||
setReportName(data.reportName);
|
||||
// Parse builder line data
|
||||
const builderLine = data.rpt_builder2_lines?.[0];
|
||||
if (builderLine) {
|
||||
const lineData = JSON.parse(builderLine.model)?.[0];
|
||||
console.log("lineData: ",lineData);
|
||||
if (lineData) {
|
||||
setAdhocList(lineData.adhoc_param_html || []);
|
||||
setDateParam(lineData.date_param_req || false);
|
||||
setSQLQuery(lineData.url || "");
|
||||
// Set dynamic fields from standard parameters
|
||||
const dynamicFields = lineData.std_param_html || [];
|
||||
setDynamicFields(dynamicFields);
|
||||
// Initialize dynamic form values
|
||||
const initialFormValues = dynamicFields.reduce((acc, field) => {
|
||||
acc[field] = "";
|
||||
return acc;
|
||||
}, {});
|
||||
setDynamicFormValues(initialFormValues);
|
||||
// Fetch data if a URL exists
|
||||
if (lineData.url) {
|
||||
fetchData(lineData.url);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching report details:", error);
|
||||
setError("Error loading report details");
|
||||
toast.error("Failed to load report details");
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const fetchData = async (url) => {
|
||||
try {
|
||||
const response = await ReportBuilderService.getAllDetailsByurl(url);
|
||||
console.log("responce of data fetch from url:", response.data)
|
||||
const data = response.data?.body ? JSON.parse(response.data.body) : [];
|
||||
setRows(data);
|
||||
setFilterRows(data);
|
||||
|
||||
// Set headers from the first row if available
|
||||
if (data.length > 0) {
|
||||
setHeaders(Object.keys(data[0]));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching data:", error);
|
||||
setError("Error loading data");
|
||||
}
|
||||
};
|
||||
|
||||
// Handle changes to adhoc parameters
|
||||
const handleAdhocChange = (index, field, value) => {
|
||||
const updatedParams = [...adhocParams];
|
||||
updatedParams[index][field] = value;
|
||||
setAdhocParams(updatedParams);
|
||||
};
|
||||
|
||||
// Add a new adhoc parameter row
|
||||
const addAdhocRow = () => {
|
||||
const lastRow = adhocParams[adhocParams.length - 1];
|
||||
|
||||
// Only add a new row if the last row has a field name
|
||||
if (lastRow && lastRow.fields_name !== '') {
|
||||
// Format the adhoc parameters for the query
|
||||
let formattedString = '';
|
||||
for (const condition of adhocParams) {
|
||||
const { andor, fields_name, condition: cond, value } = condition;
|
||||
formattedString += ` ${andor} ${fields_name} ${cond} '${value}' `;
|
||||
}
|
||||
setFormattedAdhocParameters(formattedString);
|
||||
|
||||
// Add a new row
|
||||
setAdhocParams([
|
||||
...adhocParams,
|
||||
{ andor: "AND", fields_name: "", condition: "=", value: "" },
|
||||
]);
|
||||
|
||||
// Update selected values for filtering
|
||||
selectColumn(adhocParams);
|
||||
}
|
||||
};
|
||||
|
||||
// Delete an adhoc parameter row
|
||||
const deleteAdhocRow = (index) => {
|
||||
if (adhocParams.length > 1) {
|
||||
// Get the item to be deleted
|
||||
const deletedItem = adhocParams[index];
|
||||
|
||||
// Remove the item from the adhocParams array
|
||||
const updatedParams = adhocParams.filter((_, i) => i !== index);
|
||||
setAdhocParams(updatedParams);
|
||||
|
||||
// Update selected values
|
||||
const updatedSelectedValues = { ...selectedValues };
|
||||
const columnName = deletedItem.fields_name;
|
||||
|
||||
if (updatedSelectedValues[columnName]) {
|
||||
const value = deletedItem.value;
|
||||
const indexInArray = updatedSelectedValues[columnName].indexOf(value);
|
||||
if (indexInArray !== -1) {
|
||||
updatedSelectedValues[columnName].splice(indexInArray, 1);
|
||||
|
||||
// If the array is now empty, remove the property
|
||||
if (updatedSelectedValues[columnName].length === 0) {
|
||||
delete updatedSelectedValues[columnName];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setSelectedValues(updatedSelectedValues);
|
||||
filterRowsBySelectedValues(updatedSelectedValues);
|
||||
}
|
||||
};
|
||||
|
||||
// Handle dynamic form values change
|
||||
const handleDynamicFormChange = (field, value) => {
|
||||
setDynamicFormValues({
|
||||
...dynamicFormValues,
|
||||
[field]: value
|
||||
});
|
||||
};
|
||||
|
||||
// Handle date type selection
|
||||
const handleDateSelect = (dateType) => {
|
||||
setSelectDateType(dateType);
|
||||
setFromDateQuery(null);
|
||||
setToDateQuery(null);
|
||||
|
||||
const currentDate = new Date();
|
||||
let fromDateValue, toDateValue;
|
||||
|
||||
switch (dateType) {
|
||||
case 'Today':
|
||||
fromDateValue = new Date();
|
||||
toDateValue = new Date();
|
||||
break;
|
||||
|
||||
case 'This Week':
|
||||
// Calculate this week (Monday to Sunday)
|
||||
const dayOfWeek = currentDate.getDay();
|
||||
const daysToMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1;
|
||||
|
||||
fromDateValue = new Date(currentDate);
|
||||
fromDateValue.setDate(currentDate.getDate() - daysToMonday);
|
||||
|
||||
toDateValue = new Date(fromDateValue);
|
||||
toDateValue.setDate(fromDateValue.getDate() + 6);
|
||||
break;
|
||||
|
||||
case 'Last Week':
|
||||
// Calculate last week
|
||||
const lastWeekDayOfWeek = currentDate.getDay();
|
||||
const lastWeekDaysToMonday = lastWeekDayOfWeek === 0 ? 6 : lastWeekDayOfWeek - 1;
|
||||
|
||||
fromDateValue = new Date(currentDate);
|
||||
fromDateValue.setDate(currentDate.getDate() - lastWeekDaysToMonday - 7);
|
||||
|
||||
toDateValue = new Date(fromDateValue);
|
||||
toDateValue.setDate(fromDateValue.getDate() + 6);
|
||||
break;
|
||||
|
||||
case 'This Month':
|
||||
// First day of current month
|
||||
fromDateValue = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1);
|
||||
// Last day of current month
|
||||
toDateValue = new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 0);
|
||||
break;
|
||||
|
||||
case 'Last Month':
|
||||
// First day of previous month
|
||||
fromDateValue = new Date(currentDate.getFullYear(), currentDate.getMonth() - 1, 1);
|
||||
// Last day of previous month
|
||||
toDateValue = new Date(currentDate.getFullYear(), currentDate.getMonth(), 0);
|
||||
break;
|
||||
|
||||
case 'This Year':
|
||||
// First day of current year
|
||||
fromDateValue = new Date(currentDate.getFullYear(), 0, 1);
|
||||
// Last day of current year
|
||||
toDateValue = new Date(currentDate.getFullYear(), 11, 31);
|
||||
break;
|
||||
|
||||
case 'Last Year':
|
||||
// First day of previous year
|
||||
fromDateValue = new Date(currentDate.getFullYear() - 1, 0, 1);
|
||||
// Last day of previous year
|
||||
toDateValue = new Date(currentDate.getFullYear() - 1, 11, 31);
|
||||
break;
|
||||
|
||||
default:
|
||||
fromDateValue = null;
|
||||
toDateValue = null;
|
||||
}
|
||||
|
||||
if (fromDateValue) {
|
||||
const fromDateString = fromDateValue.toISOString().substring(0, 10);
|
||||
setFromDate(fromDateString);
|
||||
setFromDateQuery(fromDateString);
|
||||
} else {
|
||||
setFromDate('');
|
||||
setFromDateQuery('');
|
||||
}
|
||||
|
||||
if (toDateValue) {
|
||||
const toDateString = toDateValue.toISOString().substring(0, 10);
|
||||
setToDate(toDateString);
|
||||
setToDateQuery(toDateString);
|
||||
} else {
|
||||
setToDate('');
|
||||
setToDateQuery('');
|
||||
}
|
||||
};
|
||||
|
||||
// Run the query
|
||||
const runQuery = () => {
|
||||
console.log("Dynamic form values:", dynamicFormValues);
|
||||
console.log("Date range:", fromDate, toDate);
|
||||
|
||||
let query = SQLQuery || '';
|
||||
|
||||
// Add standard parameters to the query
|
||||
if (Object.keys(dynamicFormValues).length > 0) {
|
||||
Object.keys(dynamicFormValues).forEach((key) => {
|
||||
if (dynamicFormValues[key] !== null && dynamicFormValues[key] !== '') {
|
||||
query += ` AND ${key} = '${dynamicFormValues[key]}'`;
|
||||
}
|
||||
});
|
||||
|
||||
// Update selected values for filtering
|
||||
selectColumn(dynamicFormValues);
|
||||
}
|
||||
|
||||
// Add date range to the query if date parameter is required
|
||||
if (dateParam) {
|
||||
// Determine the correct date key based on adhoc list
|
||||
let tempDateKey = 'createdat';
|
||||
adhocList.forEach(key => {
|
||||
if (key.includes('created_at')) tempDateKey = 'created_at';
|
||||
if (key.includes('createdAt')) tempDateKey = 'createdAt';
|
||||
});
|
||||
setDateKey(tempDateKey);
|
||||
|
||||
if (fromDate && toDate) {
|
||||
const fromDateObj = new Date(fromDate);
|
||||
const toDateObj = new Date(toDate);
|
||||
|
||||
query += ` AND ${tempDateKey} BETWEEN '${fromDateObj.toISOString().split('T')[0]}' AND '${toDateObj.toISOString().split('T')[0]}'`;
|
||||
}
|
||||
}
|
||||
|
||||
// Format and add adhoc parameters to the query
|
||||
let formattedString = '';
|
||||
for (const condition of adhocParams) {
|
||||
// Only add conditions that have a field name selected
|
||||
if (condition.fields_name) {
|
||||
const { andor, fields_name, condition: cond, value } = condition;
|
||||
formattedString += ` ${andor} ${fields_name} ${cond} '${value}' `;
|
||||
}
|
||||
}
|
||||
setFormattedAdhocParameters(formattedString);
|
||||
|
||||
// Add adhoc parameters to the query
|
||||
if (formattedString) {
|
||||
query += formattedString;
|
||||
}
|
||||
|
||||
// Update selected values for filtering based on adhoc parameters
|
||||
selectColumn(adhocParams.filter(param => param.fields_name));
|
||||
|
||||
console.log("Final query:", query);
|
||||
|
||||
// Make API request
|
||||
// axios.post('/api/report-runner/run', { query })
|
||||
// .then((response) => {
|
||||
// const data = response.data;
|
||||
|
||||
// if (data && data.length > 0) {
|
||||
// setRows(data);
|
||||
// setFilterRows(data);
|
||||
// setHeaders(Object.keys(data[0]));
|
||||
// toast.success("Query executed successfully");
|
||||
// } else {
|
||||
// toast.warning("No data returned");
|
||||
// }
|
||||
// })
|
||||
// .catch((error) => {
|
||||
// console.error("Error running query:", error);
|
||||
// toast.error("Error running the query");
|
||||
// });
|
||||
};
|
||||
|
||||
// Export file function
|
||||
const exportFile = (format) => {
|
||||
try {
|
||||
const dataToExport = filtered ? filterRows : rows;
|
||||
|
||||
if (!dataToExport || dataToExport.length === 0) {
|
||||
toast.warning("No data to export");
|
||||
return;
|
||||
}
|
||||
|
||||
const name = reportName.replace(/\s+/g, '_');
|
||||
const timestamp = new Date().toISOString().replace(/[-:T.]/g, '_').slice(0, 17);
|
||||
const fileName = `${name}_${timestamp}`;
|
||||
|
||||
// Show loading toast
|
||||
const loadingToast = toast.info("Preparing file for export...", { autoClose: false });
|
||||
|
||||
// Use the imported downloadFile service
|
||||
downloadFile(format, dataToExport, fileName);
|
||||
|
||||
// Success message
|
||||
setTimeout(() => {
|
||||
toast.dismiss(loadingToast);
|
||||
toast.success(`File exported as ${format.toUpperCase()} successfully`);
|
||||
}, 1000);
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error in export function:", error);
|
||||
toast.error("Error exporting file");
|
||||
}
|
||||
};
|
||||
|
||||
// Get table headers
|
||||
const getHeaders = () => {
|
||||
if (!rows || rows.length === 0) return [];
|
||||
|
||||
const headerSet = new Set();
|
||||
rows.forEach(row => {
|
||||
Object.keys(row).forEach(key => headerSet.add(key));
|
||||
});
|
||||
|
||||
return Array.from(headerSet);
|
||||
};
|
||||
|
||||
// Get filtered table headers
|
||||
const getFilteredHeaders = () => {
|
||||
if (!filterRows || filterRows.length === 0) return [];
|
||||
|
||||
const headerSet = new Set();
|
||||
filterRows.forEach(row => {
|
||||
Object.keys(row).forEach(key => headerSet.add(key));
|
||||
});
|
||||
|
||||
return Array.from(headerSet);
|
||||
};
|
||||
|
||||
// Check if a value is a date
|
||||
const isDate = (value) => {
|
||||
if (!value) return false;
|
||||
|
||||
if (value instanceof Date) return true;
|
||||
|
||||
if (typeof value === 'object' &&
|
||||
value.year !== undefined &&
|
||||
value.monthValue !== undefined &&
|
||||
value.dayOfMonth !== undefined) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const date = new Date(value);
|
||||
return !isNaN(date.getTime());
|
||||
};
|
||||
|
||||
// Format a date for display
|
||||
const formatDate = (value) => {
|
||||
if (!value) return '';
|
||||
|
||||
if (typeof value === 'object' &&
|
||||
value.year !== undefined &&
|
||||
value.monthValue !== undefined &&
|
||||
value.dayOfMonth !== undefined) {
|
||||
// Handle Java-style date objects
|
||||
const { year, monthValue, dayOfMonth, hour = 0, minute = 0, second = 0 } = value;
|
||||
const date = new Date(year, monthValue - 1, dayOfMonth, hour, minute, second);
|
||||
return date.toLocaleString();
|
||||
}
|
||||
|
||||
// Standard JS date
|
||||
return new Date(value).toLocaleString();
|
||||
};
|
||||
|
||||
// Process data for filtering
|
||||
const selectColumn = (data) => {
|
||||
const newSelectedValues = { ...selectedValues };
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
// Handle array of objects (like adhoc parameters)
|
||||
data.forEach(item => {
|
||||
const { fields_name, value } = item;
|
||||
|
||||
if (fields_name && fields_name.trim() !== '') {
|
||||
if (!newSelectedValues[fields_name]) {
|
||||
newSelectedValues[fields_name] = [];
|
||||
}
|
||||
|
||||
if (value !== null && value.trim() !== '') {
|
||||
if (!newSelectedValues[fields_name].includes(value)) {
|
||||
newSelectedValues[fields_name].push(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (typeof data === 'object') {
|
||||
// Handle object (like dynamic form values)
|
||||
Object.keys(data).forEach(key => {
|
||||
const value = data[key];
|
||||
|
||||
if (!newSelectedValues[key]) {
|
||||
newSelectedValues[key] = [];
|
||||
}
|
||||
|
||||
if (value !== null && value.trim && value.trim() !== '') {
|
||||
if (!newSelectedValues[key].includes(value)) {
|
||||
newSelectedValues[key].push(value);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setSelectedValues(newSelectedValues);
|
||||
filterRowsBySelectedValues(newSelectedValues);
|
||||
};
|
||||
|
||||
// Filter rows based on selected values
|
||||
const filterRowsBySelectedValues = (values = selectedValues) => {
|
||||
const filteredRows = [];
|
||||
|
||||
for (const row of rows) {
|
||||
let isMatch = true;
|
||||
|
||||
// Check each column in the selected values
|
||||
for (const columnName in values) {
|
||||
if (values.hasOwnProperty(columnName) && row.hasOwnProperty(columnName)) {
|
||||
const selectedValuesForColumn = values[columnName];
|
||||
const rowValue = row[columnName];
|
||||
|
||||
if (typeof rowValue === 'boolean') {
|
||||
// Handle boolean values
|
||||
if (selectedValuesForColumn.length === 0) continue;
|
||||
|
||||
const selectedBooleanValue = selectedValuesForColumn[0] === 'true';
|
||||
if (selectedBooleanValue !== rowValue) {
|
||||
isMatch = false;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Handle other data types
|
||||
const convertedValues = selectedValuesForColumn.map(value => {
|
||||
if (typeof rowValue === 'number') {
|
||||
return parseFloat(value);
|
||||
}
|
||||
return value;
|
||||
});
|
||||
|
||||
if (!convertedValues.includes(rowValue)) {
|
||||
isMatch = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check date range if both dates are provided
|
||||
if (fromDateQuery && toDateQuery && isMatch) {
|
||||
const from = new Date(fromDateQuery);
|
||||
const to = new Date(toDateQuery);
|
||||
|
||||
// Set hours to 0 for proper comparison
|
||||
from.setHours(0, 0, 0, 0);
|
||||
to.setHours(23, 59, 59, 999);
|
||||
|
||||
// Get the date from the row using dateKey
|
||||
const rowDate = new Date(row[dateKey]);
|
||||
|
||||
if (rowDate < from || rowDate > to) {
|
||||
isMatch = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (isMatch) {
|
||||
filteredRows.push(row);
|
||||
}
|
||||
}
|
||||
|
||||
setFilterRows(filteredRows);
|
||||
|
||||
// Determine if we are filtering or not
|
||||
const isFiltering = Object.values(values).some(arr => arr.length > 0) ||
|
||||
(fromDateQuery && toDateQuery);
|
||||
|
||||
setFiltered(isFiltering);
|
||||
};
|
||||
|
||||
// Render date cell with formatting
|
||||
const renderTableCell = (row, key) => {
|
||||
const isDateField = key === 'createdat' || key === 'createdAt' ||
|
||||
key === 'updated_at' || key === 'updatedAt' ||
|
||||
key === 'created_at' || key === 'creat_at';
|
||||
|
||||
if (isDateField && row[key]) {
|
||||
return formatDate(row[key]);
|
||||
}
|
||||
|
||||
// Handle boolean values
|
||||
if (typeof row[key] === 'boolean') {
|
||||
return row[key] ? 'True' : 'False';
|
||||
}
|
||||
|
||||
return row[key];
|
||||
};
|
||||
|
||||
// Navigate back to the reports list
|
||||
const goBack = () => {
|
||||
navigate("/admin/report-runner");
|
||||
};
|
||||
|
||||
// Add this function at an appropriate place in the component
|
||||
const toggleExportDropdown = () => {
|
||||
setShowExportDropdown(!showExportDropdown);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
{/* Report Header */}
|
||||
<h4 style={{ fontWeight: 300, display: "inline" }}>
|
||||
<b>Report Name URL - {reportName}</b>
|
||||
</h4>
|
||||
<hr />
|
||||
|
||||
{/* Date and Standard Parameters Section */}
|
||||
<div className="row">
|
||||
{/* Date Parameters */}
|
||||
{/* {dateParam && (
|
||||
<div className="col-md-6">
|
||||
<h5 style={{ fontWeight: 200, color: "black" }}>
|
||||
<b>Date Range</b>
|
||||
</h5>
|
||||
<div className="form-group mb-3">
|
||||
<label>Date Parameters</label>
|
||||
<select
|
||||
className="form-select"
|
||||
value={selectDateType}
|
||||
onChange={(e) => handleDateSelect(e.target.value)}
|
||||
>
|
||||
<option>--Select Particular--</option>
|
||||
<option>Today</option>
|
||||
<option>This Week</option>
|
||||
<option>Last Week</option>
|
||||
<option>This Month</option>
|
||||
<option>Last Month</option>
|
||||
<option>This Year</option>
|
||||
<option>Last Year</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="row">
|
||||
<div className="col-md-6">
|
||||
<label>From Date</label>
|
||||
<input
|
||||
type="date"
|
||||
className="form-control"
|
||||
value={fromDate}
|
||||
onChange={(e) => {
|
||||
setFromDate(e.target.value);
|
||||
setFromDateQuery(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<label>To Date</label>
|
||||
<input
|
||||
type="date"
|
||||
className="form-control"
|
||||
value={toDate}
|
||||
onChange={(e) => {
|
||||
setToDate(e.target.value);
|
||||
setToDateQuery(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)} */}
|
||||
|
||||
{/* Standard Parameters */}
|
||||
<div className="col-md-6">
|
||||
<h5 style={{ fontWeight: 200, color: "black" }}>
|
||||
<b>Standard Parameters</b>
|
||||
</h5>
|
||||
{dynamicFields.length === 0 ? (
|
||||
<div className="text-danger">No parameter found</div>
|
||||
) : (
|
||||
<div className="row">
|
||||
{dynamicFields.map((field, index) => (
|
||||
<div key={index} className="col-md-6 mb-3">
|
||||
<label>{field}</label>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
placeholder={`Enter ${field}`}
|
||||
value={dynamicFormValues[field] || ''}
|
||||
onChange={(e) => handleDynamicFormChange(field, e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Adhoc Parameters Section */}
|
||||
<div className="row mt-4">
|
||||
<div className="col-md-12">
|
||||
<h5 style={{ fontWeight: 200, color: "black" }}>
|
||||
<b>Adhoc Parameters</b>
|
||||
</h5>
|
||||
<table className="table">
|
||||
<tbody>
|
||||
{adhocParams.map((param, index) => (
|
||||
<tr key={index}>
|
||||
<td>
|
||||
<select
|
||||
className="form-select"
|
||||
value={param.andor}
|
||||
onChange={(e) =>
|
||||
handleAdhocChange(index, "andor", e.target.value)
|
||||
}
|
||||
>
|
||||
<option value="">Select Values</option>
|
||||
{andOrOptions.map((option, idx) => (
|
||||
<option key={idx} value={option}>
|
||||
{option}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<select
|
||||
className="form-select"
|
||||
value={param.fields_name}
|
||||
onChange={(e) =>
|
||||
handleAdhocChange(index, "fields_name", e.target.value)
|
||||
}
|
||||
>
|
||||
<option value="">Select Values</option>
|
||||
{adhocList.map((field, idx) => (
|
||||
<option key={idx} value={field}>
|
||||
{field}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<select
|
||||
className="form-select"
|
||||
value={param.condition}
|
||||
onChange={(e) =>
|
||||
handleAdhocChange(index, "condition", e.target.value)
|
||||
}
|
||||
>
|
||||
<option value="">Select Values</option>
|
||||
{conditionOptions.map((condition, idx) => (
|
||||
<option key={idx} value={condition}>
|
||||
{condition}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
value={param.value}
|
||||
onChange={(e) =>
|
||||
handleAdhocChange(index, "value", e.target.value)
|
||||
}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
className="btn btn-danger me-2"
|
||||
onClick={() => deleteAdhocRow(index)}
|
||||
title="Delete Row"
|
||||
>
|
||||
<i className="bi bi-trash"></i>
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
onClick={addAdhocRow}
|
||||
title="Add Row"
|
||||
>
|
||||
<i className="bi bi-plus"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Report Output Section */}
|
||||
<div className="row mt-4">
|
||||
<div className="col-md-6">
|
||||
<h5 style={{ fontWeight: 300 }}>
|
||||
<b>Report Output</b>
|
||||
</h5>
|
||||
</div>
|
||||
<div className="col-md-6 text-end">
|
||||
{/* Custom Export Dropdown */}
|
||||
<div
|
||||
id="export-dropdown-container"
|
||||
className="d-inline-block me-2"
|
||||
style={{ position: 'relative' }}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-outline-primary mr-3"
|
||||
onClick={toggleExportDropdown}
|
||||
>
|
||||
<i className="bi bi-download me-1"></i> Export <i className="bi bi-caret-down-fill ms-1 small "></i>
|
||||
</button>
|
||||
{showExportDropdown && (
|
||||
<div
|
||||
className="shadow position-absolute end-0 bg-white border rounded mt-1 py-1"
|
||||
style={{ zIndex: 1000, minWidth: '160px' }}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className="dropdown-item d-flex align-items-center px-3 py-2"
|
||||
onClick={() => {
|
||||
exportFile("xlsx");
|
||||
setShowExportDropdown(false);
|
||||
}}
|
||||
>
|
||||
<i className="bi bi-file-earmark-excel me-2 text-success"></i> XLSX
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="dropdown-item d-flex align-items-center px-3 py-2"
|
||||
onClick={() => {
|
||||
exportFile("csv");
|
||||
setShowExportDropdown(false);
|
||||
}}
|
||||
>
|
||||
<i className="bi bi-file-earmark-text me-2 text-primary"></i> CSV
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="dropdown-item d-flex align-items-center px-3 py-2"
|
||||
onClick={() => {
|
||||
exportFile("pdf");
|
||||
setShowExportDropdown(false);
|
||||
}}
|
||||
>
|
||||
<i className="bi bi-file-earmark-pdf me-2 text-danger"></i> PDF
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Back and Run Buttons */}
|
||||
<button className="btn btn-outline-secondary me-2" onClick={goBack}>
|
||||
Back
|
||||
</button>
|
||||
<button className="btn btn-primary" onClick={runQuery}>
|
||||
Run
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
{/* Data Table */}
|
||||
<div style={{ maxHeight: "500px", overflow: "auto", marginTop: "1rem" }}>
|
||||
{isLoading ? (
|
||||
<div className="text-center py-4">
|
||||
<div className="spinner-border text-primary" role="status"></div>
|
||||
<p className="mt-2">Loading data...</p>
|
||||
</div>
|
||||
) : error ? (
|
||||
<div className="alert alert-danger">{error}</div>
|
||||
) : (filtered ? filterRows : rows).length === 0 ? (
|
||||
<div className="alert alert-info">No data available</div>
|
||||
) : (
|
||||
<table className="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
{(filtered ? getFilteredHeaders() : getHeaders()).map((header, index) => (
|
||||
<th key={index}>{header}</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{(filtered ? filterRows : rows).map((row, rowIndex) => (
|
||||
<tr key={rowIndex}>
|
||||
{(filtered ? getFilteredHeaders() : getHeaders()).map((header, colIndex) => (
|
||||
<td key={colIndex}>
|
||||
{renderTableCell(row, header)}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ReportRunner2Edit;
|
||||
332
src/components/Dashboard/ReportRunner/ReportRunnerAll.js
Normal file
332
src/components/Dashboard/ReportRunner/ReportRunnerAll.js
Normal file
@@ -0,0 +1,332 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { toast } from "react-toastify";
|
||||
import "react-toastify/dist/ReactToastify.css";
|
||||
import { fetchAllReportsApi } from '../../../APIServices/ReportRunnerAPI';
|
||||
|
||||
|
||||
const ReportRunnerAll = () => {
|
||||
const [gridData, setGridData] = useState([]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
const [rowSelected, setRowSelected] = useState(null);
|
||||
const [modalDelete, setModalDelete] = useState(false);
|
||||
const [reports, setReports] = useState([]);
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
fetchAllReports();
|
||||
}, []);
|
||||
|
||||
// const fetchAllReports = async () => {
|
||||
// setIsLoading(true);
|
||||
// try {
|
||||
// const response = await fetch("/Report_builder/Report_builder"); // Replace with your API URL
|
||||
// const data = await response.json();
|
||||
// setGridData(data);
|
||||
// setReports(data);
|
||||
// if (data.length === 0) setError("No data Available");
|
||||
// } catch (err) {
|
||||
// console.error(err);
|
||||
// setError("Error fetching data.");
|
||||
|
||||
// } finally {
|
||||
// setIsLoading(false);
|
||||
// }
|
||||
// };
|
||||
|
||||
const fetchAllReports = async () => {
|
||||
setIsLoading(true); // Set loading state to true
|
||||
try {
|
||||
const data = await fetchAllReportsApi(); // Call the API to fetch reports
|
||||
console.log("Fetched all reports:", data);
|
||||
|
||||
setGridData(data); // Set the grid data state with the fetched reports
|
||||
setReports(data); // Set the reports state
|
||||
|
||||
if (data.length === 0) {
|
||||
setError("No data available");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error while fetching reports:", error);
|
||||
setError("Error fetching data.");
|
||||
} finally {
|
||||
setIsLoading(false); // Set loading state to false once the request is complete
|
||||
}
|
||||
};
|
||||
|
||||
const goToAdd = () => navigate("/admin/user-report");
|
||||
const goToAdd2 = () => navigate("/admin/reportbuild2all");
|
||||
|
||||
const goToRunner = (report) => {
|
||||
console.log("at time of navigating reportID: ",report.id, " report: ",report);
|
||||
if (report.isSql) {
|
||||
navigate(`/admin/report-runner1/${report.id}`,{ state: { reportId: report.id, reportData: report}});
|
||||
} else {
|
||||
navigate(`/admin/report-runner2/${report.id}`,{ state: { reportId: report.id, reportData: report}}); //reportRunnerEdit.js is called
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = async (id) => {
|
||||
setModalDelete(false);
|
||||
try {
|
||||
const response = await fetch(`/api/report-builder/${id}`, { method: "DELETE" });
|
||||
if (response.ok) {
|
||||
toast.success("Deleted successfully");
|
||||
fetchAllReports();
|
||||
} else {
|
||||
toast.error("Error deleting data.");
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
toast.error("Error deleting data.");
|
||||
}
|
||||
};
|
||||
|
||||
const openDeleteModal = (row) => {
|
||||
setRowSelected(row);
|
||||
setModalDelete(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
|
||||
{/* Header */}
|
||||
<div className="row">
|
||||
<div className="col-md-4">
|
||||
<h3><b>All Reports</b></h3>
|
||||
</div>
|
||||
<div className="col-md-8 text-end">
|
||||
<button className="btn btn-primary" onClick={goToAdd}>
|
||||
<i className="bi bi-plus"></i> Report Builder SQL
|
||||
</button>
|
||||
<button className="btn btn-primary ms-2" onClick={goToAdd2}>
|
||||
<i className="bi bi-plus"></i> Report Builder URL
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Loading Spinner */}
|
||||
{isLoading && (
|
||||
<div className="alert alert-info mt-3">
|
||||
<div className="spinner-border text-info me-2" role="status" />
|
||||
Loading...
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
{/* Report Cards */}
|
||||
{!isLoading && gridData.length > 0 && (
|
||||
<div className="row mt-4">
|
||||
{gridData.map((report, index) => (
|
||||
<div className="col-md-6 col-lg-4 mb-4" key={index}>
|
||||
<div
|
||||
className="card h-100 border-0"
|
||||
onClick={() => goToRunner(report)}
|
||||
style={{
|
||||
cursor: 'pointer',
|
||||
transition: 'all 0.3s cubic-bezier(0.165, 0.84, 0.44, 1)',
|
||||
borderRadius: '16px',
|
||||
overflow: 'hidden',
|
||||
backgroundColor: '#ffffff',
|
||||
boxShadow: '0 10px 25px rgba(0,0,0,0.05)',
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.transform = 'translateY(-8px)';
|
||||
e.currentTarget.style.boxShadow = '0 15px 30px rgba(0,0,0,0.1)';
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.transform = 'translateY(0)';
|
||||
e.currentTarget.style.boxShadow = '0 10px 25px rgba(0,0,0,0.05)';
|
||||
}}
|
||||
>
|
||||
{/* Card Header with Gradient */}
|
||||
<div
|
||||
className="d-flex justify-content-between align-items-center p-3"
|
||||
style={{
|
||||
borderBottom: '1px solid rgba(0,0,0,0.05)',
|
||||
background: report.isSql
|
||||
? 'linear-gradient(135deg, #f5f7fa 0%,rgb(198, 218, 255) 100%)'
|
||||
: 'linear-gradient(135deg, #fff9f0 0%,rgb(217, 204, 255) 100%)',
|
||||
height: '50px'
|
||||
}}
|
||||
>
|
||||
<div className="d-flex align-items-center">
|
||||
<div
|
||||
className="me-3 d-flex align-items-center justify-content-center"
|
||||
style={{
|
||||
width: '36px',
|
||||
height: '36px',
|
||||
borderRadius: '10px',
|
||||
background: report.isSql ? 'rgba(65, 105, 225, 0.15)' : 'rgba(255, 165, 0, 0.15)'
|
||||
}}
|
||||
>
|
||||
<i
|
||||
className={`bi ${report.isSql ? 'bi-database' : 'bi-link-45deg'} fs-5`}
|
||||
style={{ color: report.isSql ? '#4169E1' : '#FF8C00' }}
|
||||
></i>
|
||||
</div>
|
||||
<span
|
||||
className="fw-semibold"
|
||||
style={{
|
||||
color: report.isSql ? '#4169E1' : '#FF8C00',
|
||||
fontSize: '0.9rem'
|
||||
}}
|
||||
>
|
||||
{report.isSql == null ? "N/A" : report.isSql ? "SQL Report" : "URL Report"}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Status Indicator */}
|
||||
<div
|
||||
className="d-flex align-items-center"
|
||||
style={{
|
||||
backgroundColor: report.active ? 'rgba(40, 167, 69, 0.1)' : 'rgba(220, 53, 69, 0.1)',
|
||||
padding: '6px 12px',
|
||||
borderRadius: '30px'
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: '8px',
|
||||
height: '8px',
|
||||
borderRadius: '50%',
|
||||
backgroundColor: report.active ? '#28a745' : '#dc3545',
|
||||
marginRight: '6px'
|
||||
}}
|
||||
></div>
|
||||
<span
|
||||
style={{
|
||||
fontSize: '0.8rem',
|
||||
fontWeight: '500',
|
||||
color: report.active ? '#28a745' : '#dc3545'
|
||||
}}
|
||||
>
|
||||
{report.active ? "Active" : "Inactive"}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Card Body */}
|
||||
<div className="card-body p-4">
|
||||
<h5
|
||||
className="fw-bold mb-3"
|
||||
style={{
|
||||
fontSize: '1.15rem',
|
||||
color: '#343a40',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap'
|
||||
}}
|
||||
title={report.reportName}
|
||||
>
|
||||
{report.reportName}
|
||||
</h5>
|
||||
|
||||
<p
|
||||
style={{
|
||||
display: '-webkit-box',
|
||||
WebkitLineClamp: '2',
|
||||
WebkitBoxOrient: 'vertical',
|
||||
overflow: 'hidden',
|
||||
fontSize: '0.9rem',
|
||||
lineHeight: '1.5',
|
||||
color: '#6c757d',
|
||||
height: '42px',
|
||||
marginBottom: '0'
|
||||
}}
|
||||
title={report.description}
|
||||
>
|
||||
{report.description || "No description available"}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Card Footer */}
|
||||
<div
|
||||
className="p-3"
|
||||
style={{
|
||||
borderTop: '1px solid rgba(0,0,0,0.05)',
|
||||
backgroundColor: '#f8f9fa'
|
||||
}}
|
||||
>
|
||||
<div className="d-flex justify-content-between align-items-center">
|
||||
<div className="d-flex align-items-center" style={{ fontSize: '0.75rem', color: '#6c757d' }}>
|
||||
<i className="bi bi-clock me-1"></i>
|
||||
<span>Updated: {new Date(report.updatedAt).toLocaleDateString()}</span>
|
||||
</div>
|
||||
<div
|
||||
className="d-flex align-items-center justify-content-center"
|
||||
style={{
|
||||
width: '32px',
|
||||
height: '32px',
|
||||
borderRadius: '8px',
|
||||
backgroundColor: '#e9ecef',
|
||||
transition: 'all 0.2s ease',
|
||||
color: '#495057'
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.backgroundColor = '#dee2e6';
|
||||
e.stopPropagation();
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.backgroundColor = '#e9ecef';
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<i className="bi bi-three-dots"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
{/* Error */}
|
||||
{error && <div className="alert alert-danger mt-3">{error}</div>}
|
||||
|
||||
{/* Delete Modal */}
|
||||
{modalDelete && (
|
||||
<div className="modal show d-block" tabIndex="-1" role="dialog">
|
||||
<div className="modal-dialog">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header">
|
||||
<h5 className="modal-title">Delete Confirmation</h5>
|
||||
<button
|
||||
type="button"
|
||||
className="btn-close"
|
||||
onClick={() => setModalDelete(false)}
|
||||
/>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<p>Are you sure you want to delete the report?</p>
|
||||
<h2>{rowSelected?.id}</h2>
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-secondary"
|
||||
onClick={() => setModalDelete(false)}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-primary"
|
||||
onClick={() => handleDelete(rowSelected.id)}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ReportRunnerAll;
|
||||
787
src/components/Dashboard/ReportRunner/ReportRunnerEdit.js
Normal file
787
src/components/Dashboard/ReportRunner/ReportRunnerEdit.js
Normal file
@@ -0,0 +1,787 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { fetchStandardParameters, downloadFile } from "../../../APIServices/ReportRunnerAPI";
|
||||
import { toast } from "react-toastify";
|
||||
import './ReportrunnerEdit.css'
|
||||
|
||||
import axios from 'axios';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
|
||||
const ReportRunnerEdit = () => {
|
||||
const navigate = useNavigate();
|
||||
const { id } = useParams();
|
||||
|
||||
// Report data state
|
||||
const [reportName, setReportName] = useState("Sample Report");
|
||||
const [reportId, setReportId] = useState(id || null);
|
||||
|
||||
// Date parameters state
|
||||
const [dateParam, setDateParam] = useState(true);
|
||||
const [selectDateType, setSelectDateType] = useState("");
|
||||
const [fromDate, setFromDate] = useState("");
|
||||
const [toDate, setToDate] = useState("");
|
||||
const [fromDateQuery, setFromDateQuery] = useState("");
|
||||
const [toDateQuery, setToDateQuery] = useState("");
|
||||
|
||||
// Standard parameters state
|
||||
const [dynamicFields, setDynamicFields] = useState([]);
|
||||
const [dynamicFormValues, setDynamicFormValues] = useState({});
|
||||
|
||||
// Adhoc parameters state
|
||||
const [adhocList, setAdhocList] = useState([]);
|
||||
const [adhocParams, setAdhocParams] = useState([
|
||||
{ andor: "AND", fields_name: "", condition: "=", value: "" },
|
||||
]);
|
||||
|
||||
// Condition options
|
||||
const conditionOptions = ["=", "!=", "<", ">", "<=", ">=", "LIKE", "BETWEEN", "IN"];
|
||||
const andOrOptions = ["AND", "OR", "NOT"];
|
||||
|
||||
// Results state
|
||||
const [rows, setRows] = useState([]);
|
||||
const [filterRows, setFilterRows] = useState([]);
|
||||
const [filtered, setFiltered] = useState(false);
|
||||
const [headers, setHeaders] = useState([]);
|
||||
|
||||
// Query state
|
||||
const [SQLQuery, setSQLQuery] = useState('');
|
||||
const [dateKey, setDateKey] = useState('createdat');
|
||||
const [formattedAdhocParameters, setFormattedAdhocParameters] = useState('');
|
||||
const [selectedValues, setSelectedValues] = useState({});
|
||||
|
||||
// Loading state
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
|
||||
// Get today's date
|
||||
const todayDate = new Date().toISOString().slice(0, 10);
|
||||
|
||||
useEffect(() => {
|
||||
// Fetch report details
|
||||
if (reportId) {
|
||||
fetchReportDetails(reportId);
|
||||
}
|
||||
|
||||
// Set default date to today
|
||||
handleDateSelect('Today');
|
||||
|
||||
// Run query after a delay to allow data to load
|
||||
const timer = setTimeout(() => {
|
||||
runQuery();
|
||||
}, 2000);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, [reportId]);
|
||||
|
||||
const fetchReportDetails = async (id) => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
// This API call would need to be implemented
|
||||
const response = await axios.get(`/api/report-builder/${id}`);
|
||||
const data = response.data;
|
||||
|
||||
setReportName(data.reportName);
|
||||
|
||||
// Parse the builder line data
|
||||
const builderLine = data.rpt_builder2_lines?.[0];
|
||||
if (builderLine) {
|
||||
const lineData = JSON.parse(builderLine.model)[0];
|
||||
setAdhocList(lineData.adhoc_param_html || []);
|
||||
setDateParam(lineData.date_param_req || false);
|
||||
setSQLQuery(lineData.url || '');
|
||||
|
||||
// Set dynamic fields from standard parameters
|
||||
setDynamicFields(lineData.std_param_html || []);
|
||||
|
||||
// Initialize dynamic form values
|
||||
const initialFormValues = {};
|
||||
(lineData.std_param_html || []).forEach(field => {
|
||||
initialFormValues[field] = '';
|
||||
});
|
||||
setDynamicFormValues(initialFormValues);
|
||||
|
||||
// Fetch data based on the URL
|
||||
fetchData(lineData.url);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching report details:", error);
|
||||
setError("Error loading report details");
|
||||
toast.error("Failed to load report details");
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const fetchData = async (url) => {
|
||||
try {
|
||||
const response = await axios.get(url);
|
||||
const data = response.data?.body ? JSON.parse(response.data.body) : [];
|
||||
setRows(data);
|
||||
setFilterRows(data);
|
||||
|
||||
// Set headers from the first row if available
|
||||
if (data.length > 0) {
|
||||
setHeaders(Object.keys(data[0]));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching data:", error);
|
||||
setError("Error loading data");
|
||||
}
|
||||
};
|
||||
|
||||
// Handle changes to adhoc parameters
|
||||
const handleAdhocChange = (index, field, value) => {
|
||||
const updatedParams = [...adhocParams];
|
||||
updatedParams[index][field] = value;
|
||||
setAdhocParams(updatedParams);
|
||||
};
|
||||
|
||||
// Add a new adhoc parameter row
|
||||
const addAdhocRow = () => {
|
||||
const lastRow = adhocParams[adhocParams.length - 1];
|
||||
|
||||
// Only add a new row if the last row has a field name
|
||||
if (lastRow && lastRow.fields_name !== '') {
|
||||
// Format the adhoc parameters for the query
|
||||
let formattedString = '';
|
||||
for (const condition of adhocParams) {
|
||||
const { andor, fields_name, condition: cond, value } = condition;
|
||||
formattedString += ` ${andor} ${fields_name} ${cond} '${value}' `;
|
||||
}
|
||||
setFormattedAdhocParameters(formattedString);
|
||||
|
||||
// Add a new row
|
||||
setAdhocParams([
|
||||
...adhocParams,
|
||||
{ andor: "AND", fields_name: "", condition: "=", value: "" },
|
||||
]);
|
||||
|
||||
// Update selected values for filtering
|
||||
selectColumn(adhocParams);
|
||||
}
|
||||
};
|
||||
|
||||
// Delete an adhoc parameter row
|
||||
const deleteAdhocRow = (index) => {
|
||||
if (adhocParams.length > 1) {
|
||||
// Get the item to be deleted
|
||||
const deletedItem = adhocParams[index];
|
||||
|
||||
// Remove the item from the adhocParams array
|
||||
const updatedParams = adhocParams.filter((_, i) => i !== index);
|
||||
setAdhocParams(updatedParams);
|
||||
|
||||
// Update selected values
|
||||
const updatedSelectedValues = { ...selectedValues };
|
||||
const columnName = deletedItem.fields_name;
|
||||
|
||||
if (updatedSelectedValues[columnName]) {
|
||||
const value = deletedItem.value;
|
||||
const indexInArray = updatedSelectedValues[columnName].indexOf(value);
|
||||
if (indexInArray !== -1) {
|
||||
updatedSelectedValues[columnName].splice(indexInArray, 1);
|
||||
|
||||
// If the array is now empty, remove the property
|
||||
if (updatedSelectedValues[columnName].length === 0) {
|
||||
delete updatedSelectedValues[columnName];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setSelectedValues(updatedSelectedValues);
|
||||
filterRowsBySelectedValues(updatedSelectedValues);
|
||||
}
|
||||
};
|
||||
|
||||
// Handle dynamic form values change
|
||||
const handleDynamicFormChange = (field, value) => {
|
||||
setDynamicFormValues({
|
||||
...dynamicFormValues,
|
||||
[field]: value
|
||||
});
|
||||
};
|
||||
|
||||
// Handle date type selection
|
||||
const handleDateSelect = (dateType) => {
|
||||
setSelectDateType(dateType);
|
||||
setFromDateQuery(null);
|
||||
setToDateQuery(null);
|
||||
|
||||
const currentDate = new Date();
|
||||
let fromDateValue, toDateValue;
|
||||
|
||||
switch (dateType) {
|
||||
case 'Today':
|
||||
fromDateValue = new Date();
|
||||
toDateValue = new Date();
|
||||
break;
|
||||
|
||||
case 'This Week':
|
||||
// Calculate this week (Monday to Sunday)
|
||||
const dayOfWeek = currentDate.getDay();
|
||||
const daysToMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1;
|
||||
|
||||
fromDateValue = new Date(currentDate);
|
||||
fromDateValue.setDate(currentDate.getDate() - daysToMonday);
|
||||
|
||||
toDateValue = new Date(fromDateValue);
|
||||
toDateValue.setDate(fromDateValue.getDate() + 6);
|
||||
break;
|
||||
|
||||
case 'Last Week':
|
||||
// Calculate last week
|
||||
const lastWeekDayOfWeek = currentDate.getDay();
|
||||
const lastWeekDaysToMonday = lastWeekDayOfWeek === 0 ? 6 : lastWeekDayOfWeek - 1;
|
||||
|
||||
fromDateValue = new Date(currentDate);
|
||||
fromDateValue.setDate(currentDate.getDate() - lastWeekDaysToMonday - 7);
|
||||
|
||||
toDateValue = new Date(fromDateValue);
|
||||
toDateValue.setDate(fromDateValue.getDate() + 6);
|
||||
break;
|
||||
|
||||
case 'This Month':
|
||||
// First day of current month
|
||||
fromDateValue = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1);
|
||||
// Last day of current month
|
||||
toDateValue = new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 0);
|
||||
break;
|
||||
|
||||
case 'Last Month':
|
||||
// First day of previous month
|
||||
fromDateValue = new Date(currentDate.getFullYear(), currentDate.getMonth() - 1, 1);
|
||||
// Last day of previous month
|
||||
toDateValue = new Date(currentDate.getFullYear(), currentDate.getMonth(), 0);
|
||||
break;
|
||||
|
||||
case 'This Year':
|
||||
// First day of current year
|
||||
fromDateValue = new Date(currentDate.getFullYear(), 0, 1);
|
||||
// Last day of current year
|
||||
toDateValue = new Date(currentDate.getFullYear(), 11, 31);
|
||||
break;
|
||||
|
||||
case 'Last Year':
|
||||
// First day of previous year
|
||||
fromDateValue = new Date(currentDate.getFullYear() - 1, 0, 1);
|
||||
// Last day of previous year
|
||||
toDateValue = new Date(currentDate.getFullYear() - 1, 11, 31);
|
||||
break;
|
||||
|
||||
default:
|
||||
fromDateValue = null;
|
||||
toDateValue = null;
|
||||
}
|
||||
|
||||
if (fromDateValue) {
|
||||
const fromDateString = fromDateValue.toISOString().substring(0, 10);
|
||||
setFromDate(fromDateString);
|
||||
setFromDateQuery(fromDateString);
|
||||
} else {
|
||||
setFromDate('');
|
||||
setFromDateQuery('');
|
||||
}
|
||||
|
||||
if (toDateValue) {
|
||||
const toDateString = toDateValue.toISOString().substring(0, 10);
|
||||
setToDate(toDateString);
|
||||
setToDateQuery(toDateString);
|
||||
} else {
|
||||
setToDate('');
|
||||
setToDateQuery('');
|
||||
}
|
||||
};
|
||||
|
||||
// Run the query
|
||||
const runQuery = () => {
|
||||
console.log("Dynamic form values:", dynamicFormValues);
|
||||
console.log("Date range:", fromDate, toDate);
|
||||
|
||||
let query = SQLQuery || '';
|
||||
|
||||
// Add standard parameters to the query
|
||||
if (Object.keys(dynamicFormValues).length > 0) {
|
||||
Object.keys(dynamicFormValues).forEach((key) => {
|
||||
if (dynamicFormValues[key] !== null && dynamicFormValues[key] !== '') {
|
||||
query += ` AND ${key} = '${dynamicFormValues[key]}'`;
|
||||
}
|
||||
});
|
||||
|
||||
// Update selected values for filtering
|
||||
selectColumn(dynamicFormValues);
|
||||
}
|
||||
|
||||
// Add date range to the query if date parameter is required
|
||||
if (dateParam) {
|
||||
// Determine the correct date key based on adhoc list
|
||||
let tempDateKey = 'createdat';
|
||||
adhocList.forEach(key => {
|
||||
if (key.includes('created_at')) tempDateKey = 'created_at';
|
||||
if (key.includes('createdAt')) tempDateKey = 'createdAt';
|
||||
});
|
||||
setDateKey(tempDateKey);
|
||||
|
||||
if (fromDate && toDate) {
|
||||
const fromDateObj = new Date(fromDate);
|
||||
const toDateObj = new Date(toDate);
|
||||
|
||||
query += ` AND ${tempDateKey} BETWEEN '${fromDateObj.toISOString().split('T')[0]}' AND '${toDateObj.toISOString().split('T')[0]}'`;
|
||||
}
|
||||
}
|
||||
|
||||
// Add adhoc parameters to the query
|
||||
if (formattedAdhocParameters) {
|
||||
query += formattedAdhocParameters;
|
||||
}
|
||||
|
||||
console.log("Final query:", query);
|
||||
|
||||
// Make API request
|
||||
axios.post('/api/report-runner/run', { query })
|
||||
.then((response) => {
|
||||
const data = response.data;
|
||||
|
||||
if (data && data.length > 0) {
|
||||
setRows(data);
|
||||
setFilterRows(data);
|
||||
setHeaders(Object.keys(data[0]));
|
||||
toast.success("Query executed successfully");
|
||||
} else {
|
||||
toast.warning("No data returned");
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Error running query:", error);
|
||||
toast.error("Error running the query");
|
||||
});
|
||||
};
|
||||
|
||||
// Export file function
|
||||
const exportFile = (format) => {
|
||||
try {
|
||||
const dataToExport = filtered ? filterRows : rows;
|
||||
const name = reportName.replace(/\s+/g, '_');
|
||||
const timestamp = new Date().toISOString().replace(/[-:T.]/g, '_').slice(0, 17);
|
||||
const fileName = `${name}_${timestamp}`;
|
||||
|
||||
downloadFile(format, dataToExport, fileName);
|
||||
toast.success(`File exported as ${format.toUpperCase()} successfully`);
|
||||
} catch (error) {
|
||||
console.error("Error exporting file:", error);
|
||||
toast.error("Error exporting file");
|
||||
}
|
||||
};
|
||||
|
||||
// Get table headers
|
||||
const getHeaders = () => {
|
||||
if (!rows || rows.length === 0) return [];
|
||||
|
||||
const headerSet = new Set();
|
||||
rows.forEach(row => {
|
||||
Object.keys(row).forEach(key => headerSet.add(key));
|
||||
});
|
||||
|
||||
return Array.from(headerSet);
|
||||
};
|
||||
|
||||
// Get filtered table headers
|
||||
const getFilteredHeaders = () => {
|
||||
if (!filterRows || filterRows.length === 0) return [];
|
||||
|
||||
const headerSet = new Set();
|
||||
filterRows.forEach(row => {
|
||||
Object.keys(row).forEach(key => headerSet.add(key));
|
||||
});
|
||||
|
||||
return Array.from(headerSet);
|
||||
};
|
||||
|
||||
// Check if a value is a date
|
||||
const isDate = (value) => {
|
||||
if (!value) return false;
|
||||
|
||||
if (value instanceof Date) return true;
|
||||
|
||||
if (typeof value === 'object' &&
|
||||
value.year !== undefined &&
|
||||
value.monthValue !== undefined &&
|
||||
value.dayOfMonth !== undefined) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const date = new Date(value);
|
||||
return !isNaN(date.getTime());
|
||||
};
|
||||
|
||||
// Format a date for display
|
||||
const formatDate = (value) => {
|
||||
if (!value) return '';
|
||||
|
||||
if (typeof value === 'object' &&
|
||||
value.year !== undefined &&
|
||||
value.monthValue !== undefined &&
|
||||
value.dayOfMonth !== undefined) {
|
||||
// Handle Java-style date objects
|
||||
const { year, monthValue, dayOfMonth, hour = 0, minute = 0, second = 0 } = value;
|
||||
const date = new Date(year, monthValue - 1, dayOfMonth, hour, minute, second);
|
||||
return date.toLocaleString();
|
||||
}
|
||||
|
||||
// Standard JS date
|
||||
return new Date(value).toLocaleString();
|
||||
};
|
||||
|
||||
// Process data for filtering
|
||||
const selectColumn = (data) => {
|
||||
const newSelectedValues = { ...selectedValues };
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
// Handle array of objects (like adhoc parameters)
|
||||
data.forEach(item => {
|
||||
const { fields_name, value } = item;
|
||||
|
||||
if (fields_name && fields_name.trim() !== '') {
|
||||
if (!newSelectedValues[fields_name]) {
|
||||
newSelectedValues[fields_name] = [];
|
||||
}
|
||||
|
||||
if (value !== null && value.trim() !== '') {
|
||||
if (!newSelectedValues[fields_name].includes(value)) {
|
||||
newSelectedValues[fields_name].push(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (typeof data === 'object') {
|
||||
// Handle object (like dynamic form values)
|
||||
Object.keys(data).forEach(key => {
|
||||
const value = data[key];
|
||||
|
||||
if (!newSelectedValues[key]) {
|
||||
newSelectedValues[key] = [];
|
||||
}
|
||||
|
||||
if (value !== null && value.trim && value.trim() !== '') {
|
||||
if (!newSelectedValues[key].includes(value)) {
|
||||
newSelectedValues[key].push(value);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setSelectedValues(newSelectedValues);
|
||||
filterRowsBySelectedValues(newSelectedValues);
|
||||
};
|
||||
|
||||
// Filter rows based on selected values
|
||||
const filterRowsBySelectedValues = (values = selectedValues) => {
|
||||
const filteredRows = [];
|
||||
|
||||
for (const row of rows) {
|
||||
let isMatch = true;
|
||||
|
||||
// Check each column in the selected values
|
||||
for (const columnName in values) {
|
||||
if (values.hasOwnProperty(columnName) && row.hasOwnProperty(columnName)) {
|
||||
const selectedValuesForColumn = values[columnName];
|
||||
const rowValue = row[columnName];
|
||||
|
||||
if (typeof rowValue === 'boolean') {
|
||||
// Handle boolean values
|
||||
if (selectedValuesForColumn.length === 0) continue;
|
||||
|
||||
const selectedBooleanValue = selectedValuesForColumn[0] === 'true';
|
||||
if (selectedBooleanValue !== rowValue) {
|
||||
isMatch = false;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Handle other data types
|
||||
const convertedValues = selectedValuesForColumn.map(value => {
|
||||
if (typeof rowValue === 'number') {
|
||||
return parseFloat(value);
|
||||
}
|
||||
return value;
|
||||
});
|
||||
|
||||
if (!convertedValues.includes(rowValue)) {
|
||||
isMatch = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check date range if both dates are provided
|
||||
if (fromDateQuery && toDateQuery && isMatch) {
|
||||
const from = new Date(fromDateQuery);
|
||||
const to = new Date(toDateQuery);
|
||||
|
||||
// Set hours to 0 for proper comparison
|
||||
from.setHours(0, 0, 0, 0);
|
||||
to.setHours(23, 59, 59, 999);
|
||||
|
||||
// Get the date from the row using dateKey
|
||||
const rowDate = new Date(row[dateKey]);
|
||||
|
||||
if (rowDate < from || rowDate > to) {
|
||||
isMatch = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (isMatch) {
|
||||
filteredRows.push(row);
|
||||
}
|
||||
}
|
||||
|
||||
setFilterRows(filteredRows);
|
||||
|
||||
// Determine if we are filtering or not
|
||||
const isFiltering = Object.values(values).some(arr => arr.length > 0) ||
|
||||
(fromDateQuery && toDateQuery);
|
||||
|
||||
setFiltered(isFiltering);
|
||||
};
|
||||
|
||||
// Render date cell with formatting
|
||||
const renderTableCell = (row, key) => {
|
||||
const isDateField = key === 'createdat' || key === 'createdAt' ||
|
||||
key === 'updated_at' || key === 'updatedAt' ||
|
||||
key === 'created_at' || key === 'creat_at';
|
||||
|
||||
if (isDateField && row[key]) {
|
||||
return formatDate(row[key]);
|
||||
}
|
||||
|
||||
return row[key];
|
||||
};
|
||||
|
||||
// Navigate back to the reports list
|
||||
const goBack = () => {
|
||||
navigate("/admin/report-runner");
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
{/* Report Header */}
|
||||
<h4 style={{ fontWeight: 300, display: "inline" }}>
|
||||
<b>Report Name SQL - {reportName}</b>
|
||||
</h4>
|
||||
<hr />
|
||||
|
||||
{/* Date and Standard Parameters Section */}
|
||||
<div className="row">
|
||||
{/* Standard Parameters */}
|
||||
<div className="col-md-6">
|
||||
<h5 style={{ fontWeight: 200, color: "black" }}>
|
||||
<b>Standard Parameters</b>
|
||||
</h5>
|
||||
{dynamicFields.length === 0 ? (
|
||||
<div className="text-danger">No parameter found</div>
|
||||
) : (
|
||||
<div className="row">
|
||||
{dynamicFields.map((field, index) => (
|
||||
<div key={index} className="col-md-6 mb-3">
|
||||
<label>{field}</label>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
placeholder={`Enter ${field}`}
|
||||
value={dynamicFormValues[field] || ''}
|
||||
onChange={(e) => handleDynamicFormChange(field, e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Adhoc Parameters Section */}
|
||||
<div className="row mt-4">
|
||||
<div className="col-md-12">
|
||||
<h5 style={{ fontWeight: 200, color: "black" }}>
|
||||
<b>Adhoc Parameters</b>
|
||||
</h5>
|
||||
<table className="table">
|
||||
<tbody>
|
||||
{adhocParams.map((param, index) => (
|
||||
<tr key={index}>
|
||||
<td>
|
||||
<select
|
||||
className="form-select"
|
||||
value={param.andor}
|
||||
onChange={(e) =>
|
||||
handleAdhocChange(index, "andor", e.target.value)
|
||||
}
|
||||
>
|
||||
<option value="">Select Values</option>
|
||||
{andOrOptions.map((option, idx) => (
|
||||
<option key={idx} value={option}>
|
||||
{option}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<select
|
||||
className="form-select"
|
||||
value={param.fields_name}
|
||||
onChange={(e) =>
|
||||
handleAdhocChange(index, "fields_name", e.target.value)
|
||||
}
|
||||
>
|
||||
<option value="">Select Values</option>
|
||||
{adhocList.map((field, idx) => (
|
||||
<option key={idx} value={field}>
|
||||
{field}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<select
|
||||
className="form-select"
|
||||
value={param.condition}
|
||||
onChange={(e) =>
|
||||
handleAdhocChange(index, "condition", e.target.value)
|
||||
}
|
||||
>
|
||||
<option value="">Select Values</option>
|
||||
{conditionOptions.map((condition, idx) => (
|
||||
<option key={idx} value={condition}>
|
||||
{condition}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
value={param.value}
|
||||
onChange={(e) =>
|
||||
handleAdhocChange(index, "value", e.target.value)
|
||||
}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
className="btn btn-danger me-2"
|
||||
onClick={() => deleteAdhocRow(index)}
|
||||
title="Delete Row"
|
||||
>
|
||||
<i className="bi bi-trash"></i>
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
onClick={addAdhocRow}
|
||||
title="Add Row"
|
||||
>
|
||||
<i className="bi bi-plus"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Report Output Section */}
|
||||
<div className="row mt-4">
|
||||
<div className="col-md-6">
|
||||
<h5 style={{ fontWeight: 300 }}>
|
||||
<b>Report Output</b>
|
||||
</h5>
|
||||
</div>
|
||||
<div className="col-md-6 text-end">
|
||||
{/* Export Dropdown */}
|
||||
<div className="dropdown d-inline-block me-2">
|
||||
<button
|
||||
className="btn btn-outline-primary dropdown-toggle"
|
||||
type="button"
|
||||
id="exportDropdown"
|
||||
data-bs-toggle="dropdown"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<i className="bi bi-download"></i> Export
|
||||
</button>
|
||||
<ul className="dropdown-menu" aria-labelledby="exportDropdown">
|
||||
<li>
|
||||
<button
|
||||
className="dropdown-item"
|
||||
onClick={() => exportFile("xlsx")}
|
||||
>
|
||||
<i className="bi bi-file-earmark-excel me-2"></i> XLSX
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button
|
||||
className="dropdown-item"
|
||||
onClick={() => exportFile("csv")}
|
||||
>
|
||||
<i className="bi bi-file-earmark-text me-2"></i> CSV
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button
|
||||
className="dropdown-item"
|
||||
onClick={() => exportFile("pdf")}
|
||||
>
|
||||
<i className="bi bi-file-earmark-pdf me-2"></i> PDF
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Back and Run Buttons */}
|
||||
<button className="btn btn-outline-secondary me-2" onClick={goBack}>
|
||||
Back
|
||||
</button>
|
||||
<button className="btn btn-primary" onClick={runQuery}>
|
||||
Run
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
{/* Data Table */}
|
||||
<div style={{ maxHeight: "500px", overflow: "auto", marginTop: "1rem" }}>
|
||||
{isLoading ? (
|
||||
<div className="text-center py-4">
|
||||
<div className="spinner-border text-primary" role="status"></div>
|
||||
<p className="mt-2">Loading data...</p>
|
||||
</div>
|
||||
) : error ? (
|
||||
<div className="alert alert-danger">{error}</div>
|
||||
) : (filtered ? filterRows : rows).length === 0 ? (
|
||||
<div className="alert alert-info">No data available</div>
|
||||
) : (
|
||||
<table className="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
{(filtered ? getFilteredHeaders() : getHeaders()).map((header, index) => (
|
||||
<th key={index}>{header}</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{(filtered ? filterRows : rows).map((row, rowIndex) => (
|
||||
<tr key={rowIndex}>
|
||||
{(filtered ? getFilteredHeaders() : getHeaders()).map((header, colIndex) => (
|
||||
<td key={colIndex}>
|
||||
{renderTableCell(row, header)}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ReportRunnerEdit;
|
||||
20
src/components/Dashboard/ReportRunner/ReportrunnerEdit.css
Normal file
20
src/components/Dashboard/ReportRunner/ReportrunnerEdit.css
Normal file
@@ -0,0 +1,20 @@
|
||||
/* Grid for dropdown content */
|
||||
.dropdown-menu {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dropdown-menu.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Custom grid layout inside dropdown */
|
||||
.grid-template-columns-3 {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr); /* 3 columns */
|
||||
gap: 0.5rem; /* Spacing between buttons */
|
||||
}
|
||||
|
||||
.d-grid button {
|
||||
width: 100%; /* Make buttons span the grid cell */
|
||||
}
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useForm, useFieldArray } from 'react-hook-form';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { toast } from 'react-toastify';
|
||||
import { Container, Row, Col, Form, Button, Card } from 'react-bootstrap';
|
||||
import '../../CSS/CSS/CommonStyle.css';
|
||||
import { addReport, getKeysFromUrl } from '../../../../APIServices/reportBaseUrlAddAPI';
|
||||
|
||||
const ReportBuild2Add = () => {
|
||||
const { register, handleSubmit, control, formState: { errors } } = useForm({
|
||||
defaultValues: {
|
||||
reportName: '',
|
||||
description: '',
|
||||
active: false,
|
||||
isSql: false,
|
||||
Rpt_builder2_lines: [{ model: '' }],
|
||||
},
|
||||
});
|
||||
|
||||
const { fields, append } = useFieldArray({
|
||||
control,
|
||||
name: 'Rpt_builder2_lines',
|
||||
});
|
||||
|
||||
const [keysFromUrl, setKeysFromUrl] = useState([]);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleGetKeys = async (url) => {
|
||||
if (!url) {
|
||||
toast.error('URL is required');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const data = await getKeysFromUrl(url);
|
||||
setKeysFromUrl(data);
|
||||
toast.success('Keys retrieved successfully');
|
||||
} catch (error) {
|
||||
toast.error(error || 'Failed to retrieve keys');
|
||||
}
|
||||
};
|
||||
|
||||
// const onSubmit = async (formData) => {
|
||||
// try {
|
||||
// const { status } = await addReport(formData);
|
||||
// if (status >= 200 && status <= 209) {
|
||||
// toast.success('Report saved successfully');
|
||||
// navigate('/admin/reportbuild2all');
|
||||
// } else {
|
||||
// toast.error('Report save unsuccessful');
|
||||
// }
|
||||
// } catch (error) {
|
||||
// toast.error(error || 'Error saving report');
|
||||
// }
|
||||
// };
|
||||
const onSubmit = async (formData) => {
|
||||
// Prepare the formData to match the backend API requirements
|
||||
const updatedData = {
|
||||
reportName: formData.reportName, // Use the correct field name
|
||||
description: formData.description,
|
||||
active: formData.active || false, // Ensure `active` is passed and defaults to `false`
|
||||
isSql: formData.isSql || false, // Ensure `isSql` is passed and defaults to `false`
|
||||
Rpt_builder2_lines: formData.Rpt_builder2_lines || [] // Default to empty array if not provided
|
||||
};
|
||||
|
||||
|
||||
try {
|
||||
const { status } = await addReport(updatedData);
|
||||
console.log("Report added successfully...")
|
||||
if (status >= 200 && status <= 209) {
|
||||
toast.success('Report saved successfully');
|
||||
console.log('Report Saved successfully...')
|
||||
navigate('/admin/reportbuild2all');
|
||||
} else {
|
||||
toast.error('Report save unsuccessful');
|
||||
}
|
||||
} catch (error) {
|
||||
toast.error(error || 'Error saving report');
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
navigate('/admin/reportbuild2all');
|
||||
};
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Card className="shadow-sm mb-4">
|
||||
<Card.Header className="d-flex justify-content-between align-items-center">
|
||||
<h4 className="m-0" style={{ fontWeight: 400, color: '#0E6591' }}>
|
||||
<b>Create New Report</b>
|
||||
</h4>
|
||||
<span className="badge bg-info">Add Mode</span>
|
||||
</Card.Header>
|
||||
<Card.Body>
|
||||
<Form onSubmit={handleSubmit(onSubmit)}>
|
||||
<Row className="mb-3">
|
||||
<Col md={4} sm={12}>
|
||||
<Form.Group controlId="reportName">
|
||||
<Form.Label>Report Name</Form.Label>
|
||||
<Form.Control
|
||||
type="text"
|
||||
placeholder="Enter name"
|
||||
{...register('reportName', { required: 'Report name is required' })}
|
||||
isInvalid={errors.reportName}
|
||||
className="custom-hover-border"
|
||||
/>
|
||||
<Form.Control.Feedback type="invalid">
|
||||
{errors.reportName?.message}
|
||||
</Form.Control.Feedback>
|
||||
</Form.Group>
|
||||
</Col>
|
||||
<Col md={4} sm={12}>
|
||||
<Form.Group controlId="description">
|
||||
<Form.Label>Description</Form.Label>
|
||||
<Form.Control
|
||||
as="textarea"
|
||||
rows={2}
|
||||
placeholder="Enter Description"
|
||||
{...register('description')}
|
||||
className="custom-hover-border"
|
||||
/>
|
||||
</Form.Group>
|
||||
</Col>
|
||||
<Col md={4} sm={12}>
|
||||
<Form.Group controlId="active">
|
||||
<Form.Label>Active</Form.Label>
|
||||
<Form.Check type="checkbox" label="Active" {...register('active')} />
|
||||
</Form.Group>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row>
|
||||
{fields.map((item, index) => (
|
||||
<Col key={item.id} md={4} sm={12}>
|
||||
{/* <Form.Group controlId={`Rpt_builder2_lines.${index}.model`}>
|
||||
<Form.Label>Model</Form.Label>
|
||||
<Form.Control
|
||||
type="text"
|
||||
placeholder="Enter model"
|
||||
{...register(`Rpt_builder2_lines.${index}.model`)}
|
||||
className="custom-hover-border"
|
||||
/>
|
||||
</Form.Group> */}
|
||||
</Col>
|
||||
))}
|
||||
<Col md={12} className="d-flex justify-content-start mt-3">
|
||||
{/* <Button
|
||||
variant="outline"
|
||||
onClick={() => append({ model: '' })}
|
||||
className="custom_manage_column_button"
|
||||
>
|
||||
Add Line
|
||||
</Button> */}
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row className="mt-4">
|
||||
<Col className="d-flex justify-content-center">
|
||||
<div className="btn-group">
|
||||
<Button variant="secondary" onClick={handleCancel}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button variant="primary" type="submit" className="ms-2 custom_button" onClick={onSubmit}>
|
||||
Submit
|
||||
</Button>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
export default ReportBuild2Add;
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
.content-container{
|
||||
margin-top: 10rem !important;
|
||||
}
|
||||
@@ -0,0 +1,602 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { toast } from "react-toastify";
|
||||
import "react-toastify/dist/ReactToastify.css";
|
||||
import "./Report-buildall.css";
|
||||
import { getReports } from "../../../../APIServices/reportBaseUrlAllAPI";
|
||||
import { deleteReport } from "APIServices/reportBaseUrlDeleteAPI";
|
||||
import { addReport } from "APIServices/reportBaseUrlAddAPI";
|
||||
import ConfirmModal from "../../../common/ConfirmModal";
|
||||
import {
|
||||
Table,
|
||||
Pagination,
|
||||
PaginationItem,
|
||||
PaginationLink,
|
||||
Badge,
|
||||
} from "reactstrap";
|
||||
import {
|
||||
Button,
|
||||
Form,
|
||||
Row,
|
||||
Col,
|
||||
InputGroup,
|
||||
FormControl,
|
||||
Modal,
|
||||
Dropdown,
|
||||
} from "react-bootstrap";
|
||||
import { BsJournals } from "react-icons/bs";
|
||||
import Spinner from "../../../../UIComponants/Spinner";
|
||||
import "react-toastify/dist/ReactToastify.css";
|
||||
import "../../../Dashboard/CSS/CSS/CommonStyle.css";
|
||||
import {
|
||||
faTrash,
|
||||
faPlus,
|
||||
faGripVertical,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { FaSearch, FaTimes } from "react-icons/fa";
|
||||
|
||||
const ReportBuild2All = () => {
|
||||
const [gridData, setGridData] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
const [rowSelected, setRowSelected] = useState(null);
|
||||
const [visibleColumns, setVisibleColumns] = useState({
|
||||
goTo: true,
|
||||
reportName: true,
|
||||
description: true,
|
||||
active: true,
|
||||
action: true,
|
||||
});
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [recordsPerPage, setRecordsPerPage] = useState(10);
|
||||
const navigate = useNavigate();
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
|
||||
// Add new state variables for modals
|
||||
const [showAddModal, setShowAddModal] = useState(false);
|
||||
const [showConfirmModal, setShowConfirmModal] = useState(false);
|
||||
const [deleteItem, setDeleteItem] = useState({ id: null, reportName: "" });
|
||||
|
||||
// Add state for new data
|
||||
const [newData, setNewData] = useState({
|
||||
reportName: "",
|
||||
description: "",
|
||||
active: false,
|
||||
isSql: false,
|
||||
Rpt_builder2_lines: [{ model: "" }]
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
getAllReports();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate loading data
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
}, 3000); // Simulated 3 seconds loading
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
// Add custom styles to override Bootstrap's active styling
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
.dropdown-item-custom:active,
|
||||
.dropdown-item-custom:focus,
|
||||
.dropdown-item-custom.active {
|
||||
background-color: white !important;
|
||||
color: #333 !important;
|
||||
}
|
||||
.dropdown-item-custom:hover {
|
||||
background-color: #f8f9fa !important;
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
|
||||
return () => {
|
||||
document.head.removeChild(style);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const filteredData = gridData
|
||||
.filter(
|
||||
(item) =>
|
||||
item.reportName &&
|
||||
item.reportName.toLowerCase().includes(searchQuery.toLowerCase())
|
||||
)
|
||||
.slice((currentPage - 1) * recordsPerPage, currentPage * recordsPerPage);
|
||||
|
||||
const handlePageChange = (pageNumber) => {
|
||||
setCurrentPage(pageNumber);
|
||||
};
|
||||
const totalPages = Math.ceil(gridData.length / recordsPerPage);
|
||||
const handleRecordsPerPageChange = (value) => {
|
||||
setRecordsPerPage(value);
|
||||
setCurrentPage(1);
|
||||
};
|
||||
|
||||
const getAllReports = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const data = await getReports(); // Use the new API service
|
||||
if (data.length === 0) {
|
||||
setError("No data Available");
|
||||
} else {
|
||||
setGridData(data);
|
||||
console.log("data is coming", data);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Error fetching reports:", err);
|
||||
setError("Failed to fetch data");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const goToRunner = () => {
|
||||
navigate("/admin/report-runner");
|
||||
};
|
||||
|
||||
const goToAdd = () => {
|
||||
setShowAddModal(true);
|
||||
};
|
||||
|
||||
const goToLines = (report) => {
|
||||
console.log("Complete Report Data:", report); // Log full report data
|
||||
console.log("Rpt_builder2_lines:", report?.Rpt_builder2_lines); // Log Rpt_builder2_lines specifically
|
||||
|
||||
navigate(`/admin/reportbuild2edit`, { state: { reportId: report.id, report: report } });
|
||||
|
||||
// // Check if Rpt_builder2_lines is an array and has at least one item
|
||||
// if (Array.isArray(report?.Rpt_builder2_lines) && report.Rpt_builder2_lines.length > 0) {
|
||||
// const firstLine = report.Rpt_builder2_lines[0];
|
||||
|
||||
// // Check if the model field in the first item is not empty
|
||||
// if (firstLine?.model !== '') {
|
||||
// navigate(`/admin/reportbuild2edit`, { state: { reportId: report.id } });
|
||||
// } else {
|
||||
// navigate(`/admin/reportbuild2edit`, { state: { reportId: report.id } });
|
||||
// }
|
||||
// } else {
|
||||
// // If Rpt_builder2_lines is missing or not an array, just navigate to /admin/reportbuild2edit
|
||||
// console.warn("Rpt_builder2_lines is missing or not an array. Navigating to /admin/reportbuild2edit...");
|
||||
// navigate(`/admin/reportbuild2edit`, { state: { reportId: report.id } });
|
||||
// }
|
||||
};
|
||||
|
||||
const confirmDelete = (e, report) => {
|
||||
e.stopPropagation(); // Prevent any parent handlers from being called
|
||||
setDeleteItem({
|
||||
id: report.id,
|
||||
reportName: report.reportName || "this report"
|
||||
});
|
||||
setShowConfirmModal(true);
|
||||
};
|
||||
|
||||
const toggleColumn = (column) => {
|
||||
setVisibleColumns((prev) => ({
|
||||
...prev,
|
||||
[column]: !prev[column],
|
||||
}));
|
||||
};
|
||||
|
||||
const handleDelete = async () => {
|
||||
const { id } = deleteItem;
|
||||
if (!id) {
|
||||
console.error("ID is undefined. Cannot delete.");
|
||||
toast.error("Invalid ID. Deletion failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Call the deleteReport service
|
||||
const response = await deleteReport(id);
|
||||
|
||||
console.log("Delete response:", response);
|
||||
|
||||
// Check if delete was successful
|
||||
if (response && (response.status === 200 || response.status === 204)) {
|
||||
toast.success("Report deleted successfully");
|
||||
|
||||
// Remove the deleted item from the state
|
||||
setGridData(gridData.filter(item => item.id !== id));
|
||||
} else {
|
||||
toast.error("Error deleting data: Unexpected response");
|
||||
}
|
||||
|
||||
// Hide the confirm modal
|
||||
setShowConfirmModal(false);
|
||||
} catch (err) {
|
||||
console.error("Error deleting report:", err);
|
||||
toast.error("Error deleting data");
|
||||
setShowConfirmModal(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleAddSubmit = async () => {
|
||||
try {
|
||||
// Validate required fields
|
||||
if (!newData.reportName.trim()) {
|
||||
toast.warning("Report name is required");
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare payload for API call
|
||||
const payload = {
|
||||
reportName: newData.reportName,
|
||||
description: newData.description,
|
||||
active: newData.active,
|
||||
isSql: false,
|
||||
Rpt_builder2_lines: [{ model: "" }],
|
||||
};
|
||||
|
||||
console.log("payload to be submitted for add url report: ", payload)
|
||||
|
||||
// Call the API to add the report
|
||||
const response = await addReport(payload);
|
||||
console.log("responce:",response)
|
||||
// Check if we received a proper response
|
||||
if (response && response.data && response.data.id) {
|
||||
toast.success("Report added successfully");
|
||||
|
||||
// Add just the report data to the grid data (not the entire response)
|
||||
setGridData([...gridData, response.data]);
|
||||
|
||||
// Reset form and close modal
|
||||
setNewData({
|
||||
reportName: "",
|
||||
description: "",
|
||||
active: false,
|
||||
isSql: false,
|
||||
Rpt_builder2_lines: [{ model: "" }]
|
||||
});
|
||||
setShowAddModal(false);
|
||||
|
||||
// Refresh the report list to ensure data consistency
|
||||
getAllReports();
|
||||
} else {
|
||||
toast.error("Failed to add report: Unexpected response");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error adding report:", error);
|
||||
toast.error("Failed to add report");
|
||||
}
|
||||
};
|
||||
|
||||
const handleAddClose = () => {
|
||||
setShowAddModal(false);
|
||||
};
|
||||
|
||||
// Add custom CSS to override Bootstrap's active dropdown item styling
|
||||
const dropdownItemStyle = {
|
||||
backgroundColor: 'white',
|
||||
color: '#333',
|
||||
cursor: 'pointer',
|
||||
padding: '8px 12px',
|
||||
border: 'none',
|
||||
outline: 'none !important'
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
{loading ? (
|
||||
<Spinner /> // The spinner will be centered in the viewport
|
||||
) : (
|
||||
<div className="content-container ">
|
||||
<div className="container-fluid mb-7">
|
||||
<div className="row mb-3">
|
||||
<div className="col-md-4 title_name">
|
||||
<h3>
|
||||
<b>Report Builder2 (URL) </b>
|
||||
</h3>
|
||||
</div>
|
||||
<div className="col-md-8 text-end">
|
||||
<button className="btn btn-primary me-2" onClick={goToRunner}>
|
||||
<i className="bi bi-grid-fill"></i> Report Runner
|
||||
</button>
|
||||
<button className="btn btn-primary" onClick={goToAdd}>
|
||||
<i className="bi bi-plus-lg"></i> ADD
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* DataGrid */}
|
||||
{loading ? (
|
||||
<div>Loading...</div>
|
||||
) : error ? (
|
||||
<div className="text-danger">{error}</div>
|
||||
) : (
|
||||
<Table
|
||||
striped
|
||||
responsive
|
||||
hover
|
||||
className="align-middle table-flush shadow-sm"
|
||||
>
|
||||
<thead className="custom_header">
|
||||
<tr>
|
||||
{visibleColumns.goTo && <th>Go To</th>}
|
||||
{visibleColumns.reportName && <th>Report Name</th>}
|
||||
{visibleColumns.description && <th>Report Description</th>}
|
||||
{visibleColumns.active && <th className="text-center">Active</th>}
|
||||
{visibleColumns.action && <th className="text-center">Action</th>}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="tbody">
|
||||
{filteredData.length === 0 ? (
|
||||
<tr>
|
||||
<td
|
||||
colSpan={
|
||||
Object.values(visibleColumns).filter(Boolean).length
|
||||
}
|
||||
className="text-center"
|
||||
>
|
||||
No data available
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
filteredData.filter((user) => user.isSql === false).map((user) => (
|
||||
<tr key={user.id}>
|
||||
{visibleColumns.goTo && (
|
||||
<td>
|
||||
<Badge
|
||||
color="primary"
|
||||
pill
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
padding: "0.5em 1em",
|
||||
fontSize: "0.9rem",
|
||||
textTransform: "uppercase",
|
||||
}}
|
||||
onClick={() => goToLines(user)}
|
||||
>
|
||||
Set Up
|
||||
</Badge>
|
||||
</td>
|
||||
)}
|
||||
{visibleColumns.reportName && <td>{user.reportName}</td>}
|
||||
{visibleColumns.description && <td>{user.description}</td>}
|
||||
{visibleColumns.active && (
|
||||
<td
|
||||
style={{
|
||||
fontWeight: user.active ? "bold" : "normal",
|
||||
color: user.active ? "green" : "red",
|
||||
backgroundColor: user.active
|
||||
? "rgba(0, 128, 0, 0.1)"
|
||||
: "rgba(255, 0, 0, 0.1)",
|
||||
padding: "2px 4px",
|
||||
display: "inline-block",
|
||||
borderRadius: "4px",
|
||||
textAlign: "center",
|
||||
marginTop: "10px",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
{user.active ? "Yes" : "No"}
|
||||
</td>
|
||||
)}
|
||||
{visibleColumns.action && (
|
||||
<td>
|
||||
<FontAwesomeIcon
|
||||
icon={faTrash}
|
||||
onClick={(e) => confirmDelete(e, user)}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
fontSize: "1rem",
|
||||
color: "red",
|
||||
marginRight: "15px",
|
||||
}}
|
||||
/>
|
||||
</td>
|
||||
)}
|
||||
</tr>
|
||||
))
|
||||
)}
|
||||
</tbody>
|
||||
</Table>
|
||||
)}
|
||||
|
||||
{/* 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>
|
||||
<Dropdown.Header className="fw-bold text-dark">Toggle Column Visibility</Dropdown.Header>
|
||||
<Dropdown.Divider />
|
||||
{[
|
||||
{ key: 'goTo', label: 'Go To' },
|
||||
{ key: 'reportName', label: 'Report Name' },
|
||||
{ key: 'description', label: 'Report Description' },
|
||||
{ key: 'active', label: 'Active Status' },
|
||||
{ key: 'action', label: 'Actions' }
|
||||
].map(column => (
|
||||
<Dropdown.Item
|
||||
key={column.key}
|
||||
onClick={() => toggleColumn(column.key)}
|
||||
style={dropdownItemStyle}
|
||||
className="dropdown-item-custom"
|
||||
>
|
||||
<Form.Check
|
||||
type="checkbox"
|
||||
label={column.label}
|
||||
checked={visibleColumns[column.key]}
|
||||
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"
|
||||
>
|
||||
<Dropdown.Header className="fw-bold text-dark">Records Per Page</Dropdown.Header>
|
||||
<Dropdown.Divider />
|
||||
{[1, 5, 10, 20, 50].map((number) => (
|
||||
<Dropdown.Item
|
||||
key={number}
|
||||
onClick={() => handleRecordsPerPageChange(number)}
|
||||
style={dropdownItemStyle}
|
||||
className="dropdown-item-custom d-flex justify-content-between align-items-center"
|
||||
>
|
||||
<span className={recordsPerPage === number ? 'fw-bold' : ''}>{number}</span>
|
||||
{recordsPerPage === number && (
|
||||
<i className="fa fa-check-circle text-primary" />
|
||||
)}
|
||||
</Dropdown.Item>
|
||||
))}
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Pagination className="pagination mt-3">
|
||||
<PaginationItem disabled={currentPage === 1}>
|
||||
<PaginationLink
|
||||
previous
|
||||
onClick={() => handlePageChange(currentPage - 1)}
|
||||
/>
|
||||
</PaginationItem>
|
||||
{[...Array(totalPages).keys()].map((pageNumber) => (
|
||||
<PaginationItem
|
||||
key={pageNumber + 1}
|
||||
active={currentPage === pageNumber + 1}
|
||||
>
|
||||
<PaginationLink
|
||||
onClick={() => handlePageChange(pageNumber + 1)}
|
||||
>
|
||||
{pageNumber + 1}
|
||||
</PaginationLink>
|
||||
</PaginationItem>
|
||||
))}
|
||||
<PaginationItem disabled={currentPage === totalPages}>
|
||||
<PaginationLink
|
||||
next
|
||||
onClick={() => handlePageChange(currentPage + 1)}
|
||||
/>
|
||||
</PaginationItem>
|
||||
</Pagination>
|
||||
|
||||
{/* Add Modal */}
|
||||
<Modal show={showAddModal} onHide={handleAddClose} centered>
|
||||
<Modal.Header>
|
||||
<Modal.Title style={{ color: "#0E6591" }}>
|
||||
Add New Report
|
||||
</Modal.Title>
|
||||
<div
|
||||
onClick={handleAddClose}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
color: "#0E6591",
|
||||
fontSize: "1.5rem",
|
||||
position: "absolute",
|
||||
right: "15px",
|
||||
top: "15px",
|
||||
}}
|
||||
aria-label="Close"
|
||||
>
|
||||
<FaTimes />
|
||||
</div>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
<Form>
|
||||
<Form.Group controlId="formGroupAddName" className="mb-3">
|
||||
<Form.Label className="fw-bold">Report Name</Form.Label>
|
||||
<Form.Control
|
||||
type="text"
|
||||
placeholder="Enter report name"
|
||||
value={newData.reportName}
|
||||
onChange={(e) =>
|
||||
setNewData({ ...newData, reportName: e.target.value })
|
||||
}
|
||||
className="custom-hover-border shadow-sm"
|
||||
required
|
||||
/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group
|
||||
controlId="formGroupAddDescription"
|
||||
className="mb-3"
|
||||
>
|
||||
<Form.Label className="fw-bold">Description</Form.Label>
|
||||
<Form.Control
|
||||
type="text"
|
||||
placeholder="Enter description"
|
||||
value={newData.description}
|
||||
onChange={(e) =>
|
||||
setNewData({ ...newData, description: e.target.value })
|
||||
}
|
||||
className="custom-hover-border shadow-sm"
|
||||
required
|
||||
/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group controlId="formGroupAddActive" className="mb-3">
|
||||
<Form.Check
|
||||
type="checkbox"
|
||||
label="Active"
|
||||
checked={newData.active}
|
||||
onChange={(e) =>
|
||||
setNewData({ ...newData, active: e.target.checked })
|
||||
}
|
||||
className="custom-checkbox shadow-sm"
|
||||
/>
|
||||
</Form.Group>
|
||||
</Form>
|
||||
</Modal.Body>
|
||||
<Modal.Footer className="justify-content-between">
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={handleAddClose}
|
||||
className="custom_button px-4"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={handleAddSubmit}
|
||||
className="custom_button px-4"
|
||||
>
|
||||
Add Report
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
|
||||
{/* Confirmation Modal */}
|
||||
<ConfirmModal
|
||||
show={showConfirmModal}
|
||||
onHide={() => setShowConfirmModal(false)}
|
||||
onConfirm={handleDelete}
|
||||
title="Confirm Deletion"
|
||||
message={`Are you sure you want to delete "${deleteItem.reportName}"? This action cannot be undone.`}
|
||||
confirmLabel="Delete"
|
||||
cancelLabel="Cancel"
|
||||
variant="danger"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ReportBuild2All;
|
||||
@@ -0,0 +1,448 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import ReportBuilderService from "../../../../APIServices/ReportBuilderService";
|
||||
import { toast } from "react-toastify";
|
||||
import {
|
||||
Container,
|
||||
Row,
|
||||
Col,
|
||||
Form,
|
||||
FormGroup,
|
||||
Label,
|
||||
Input,
|
||||
Button,
|
||||
Badge,
|
||||
} from "reactstrap";
|
||||
import { useNavigate, useLocation, useParams } from "react-router-dom";
|
||||
import Multiselect from 'multiselect-react-dropdown';
|
||||
|
||||
/**
|
||||
* ReportBuild2 Component
|
||||
*
|
||||
* This component provides an interface for editing report configurations
|
||||
* that are based on URL endpoints. It allows users to:
|
||||
* - Configure a URL endpoint for data
|
||||
* - Set parameters for the report
|
||||
* - Enable/disable date filtering
|
||||
* - Save configuration changes
|
||||
*/
|
||||
const ReportBuild2 = () => {
|
||||
// Get report ID from URL params or location state
|
||||
const location = useLocation();
|
||||
const params = useParams();
|
||||
const reportId = params.id || location.state?.reportId;
|
||||
const navigate = useNavigate();
|
||||
|
||||
// State for form data and related configuration
|
||||
const [entryForm, setEntryForm] = useState({
|
||||
id: reportId,
|
||||
url: "",
|
||||
date_param_req: false,
|
||||
std_param_html: [],
|
||||
adhoc_param_html: "",
|
||||
});
|
||||
|
||||
// State for UI data and API responses
|
||||
const [keysFromUrl, setKeysFromUrl] = useState([]); // Parameters available from URL
|
||||
const [databaseList, setDatabaseList] = useState([]); // Available databases
|
||||
const [lineId, setLineId] = useState(null); // ID for the report line being edited
|
||||
const [builderLineData, setBuilderLineData] = useState([]); // Existing report configuration
|
||||
|
||||
/**
|
||||
* Load report data when component mounts or reportId changes
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (reportId) {
|
||||
getReportById(reportId);
|
||||
}
|
||||
}, [reportId]);
|
||||
|
||||
/**
|
||||
* Fetch database list from API
|
||||
* Currently not called by default but available for future use
|
||||
*/
|
||||
const fetchDatabaseList = async () => {
|
||||
try {
|
||||
const response = await ReportBuilderService.getDatabase();
|
||||
if (response && response.data) {
|
||||
setDatabaseList(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to load database list:", error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get report details by ID and populate the form
|
||||
* @param {string} id - Report ID to fetch
|
||||
*/
|
||||
const getReportById = async (id) => {
|
||||
try {
|
||||
const response = await ReportBuilderService.getrbDetailsById(id);
|
||||
|
||||
if (!response || !response.data) {
|
||||
console.error("No report data found");
|
||||
return;
|
||||
}
|
||||
|
||||
const report = response.data;
|
||||
const lineData = report.rpt_builder2_lines || [];
|
||||
|
||||
if (lineData.length === 0) {
|
||||
console.warn("No report line data found");
|
||||
return;
|
||||
}
|
||||
|
||||
// Store line ID for update operations
|
||||
setLineId(lineData[0].id);
|
||||
|
||||
// Parse model data if exists
|
||||
if (lineData[0].model && lineData[0].model !== '') {
|
||||
try {
|
||||
const parsedData = JSON.parse(lineData[0].model);
|
||||
setBuilderLineData(parsedData);
|
||||
|
||||
if (parsedData && parsedData.length > 0) {
|
||||
// Handle std_param_html - ensure it's always an array
|
||||
const stdParams = parsedData[0].std_param_html || [];
|
||||
const stdParamsArray = Array.isArray(stdParams) ? stdParams :
|
||||
(typeof stdParams === 'string' && stdParams.length > 0) ? [stdParams] : [];
|
||||
|
||||
// Update form with existing data
|
||||
setEntryForm({
|
||||
id: lineId,
|
||||
std_param_html: stdParamsArray,
|
||||
adhoc_param_html: parsedData[0].adhoc_param_html || "",
|
||||
date_param_req: parsedData[0].date_param_req || false,
|
||||
url: parsedData[0].url || "",
|
||||
});
|
||||
}
|
||||
} catch (parseError) {
|
||||
console.error("Error parsing report data:", parseError);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to load report details:", error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle form field changes for standard input types
|
||||
* @param {Event} e - Input change event
|
||||
*/
|
||||
const handleInputChange = (e) => {
|
||||
const { name, value, type, checked } = e.target;
|
||||
|
||||
if (type === "select-multiple") {
|
||||
// Handle multiple select inputs
|
||||
const selectedOptions = Array.from(e.target.selectedOptions, option => option.value);
|
||||
setEntryForm(prev => ({
|
||||
...prev,
|
||||
[name]: selectedOptions
|
||||
}));
|
||||
} else {
|
||||
// Handle regular inputs and checkboxes
|
||||
setEntryForm(prev => ({
|
||||
...prev,
|
||||
[name]: type === "checkbox" ? checked : value,
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle Multiselect onSelect event for Standard Parameters
|
||||
* @param {Array} selectedList - List of selected items
|
||||
*/
|
||||
const handleMultiselectSelect = (selectedList) => {
|
||||
setEntryForm(prev => ({
|
||||
...prev,
|
||||
std_param_html: selectedList
|
||||
}));
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle Multiselect onRemove event for Standard Parameters
|
||||
* @param {Array} selectedList - List of selected items after removal
|
||||
*/
|
||||
const handleMultiselectRemove = (selectedList) => {
|
||||
setEntryForm(prev => ({
|
||||
...prev,
|
||||
std_param_html: selectedList
|
||||
}));
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetch column keys from provided URL
|
||||
* Makes API call to getcolumnDetailsByurl to get available parameters
|
||||
* @param {string} url - URL to fetch columns from
|
||||
*/
|
||||
const getKeys = async (url) => {
|
||||
if (!url) {
|
||||
toast.info("Please enter a URL");
|
||||
return;
|
||||
}
|
||||
|
||||
const loadingToastId = toast.info("Loading...");
|
||||
|
||||
try {
|
||||
const response = await ReportBuilderService.getcolumnDetailsByurl(url);
|
||||
console.log("response: ", response);
|
||||
toast.dismiss(loadingToastId);
|
||||
|
||||
if (response && response.data) {
|
||||
// Update keysFromUrl with the response data
|
||||
setKeysFromUrl(response.data);
|
||||
// Update adhoc_param_html in the form
|
||||
setEntryForm(prev => ({
|
||||
...prev,
|
||||
adhoc_param_html: response.data
|
||||
}));
|
||||
toast.success("Success");
|
||||
} else {
|
||||
console.warn("No keys found in the URL response");
|
||||
toast.info("No data found");
|
||||
}
|
||||
} catch (error) {
|
||||
toast.dismiss(loadingToastId);
|
||||
console.error("Failed to fetch keys:", error);
|
||||
toast.error("Operation failed");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle form submission
|
||||
* Validates input and saves report configuration
|
||||
* @param {Event} e - Form submit event
|
||||
*/
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
// Validate required fields
|
||||
if (!entryForm.url) {
|
||||
toast.info("URL is required");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!lineId) {
|
||||
console.error("Missing report line ID");
|
||||
toast.error("Operation failed");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Prepare updated builder line data
|
||||
const updatedBuilderLineData = [...builderLineData];
|
||||
|
||||
// If no existing data, create a new entry
|
||||
if (updatedBuilderLineData.length === 0) {
|
||||
updatedBuilderLineData.push({});
|
||||
}
|
||||
|
||||
// Update the first line data
|
||||
updatedBuilderLineData[0] = {
|
||||
std_param_html: entryForm.std_param_html,
|
||||
adhoc_param_html: entryForm.adhoc_param_html,
|
||||
date_param_req: entryForm.date_param_req,
|
||||
url: entryForm.url,
|
||||
};
|
||||
|
||||
// Prepare payload for API
|
||||
const payload = {
|
||||
model: JSON.stringify(updatedBuilderLineData)
|
||||
};
|
||||
|
||||
// Call API to update report
|
||||
await ReportBuilderService.updaterbLineData(payload, lineId);
|
||||
toast.success("Report updated successfully");
|
||||
|
||||
// Navigate back to the list
|
||||
navigate("/admin/reportbuild2all");
|
||||
} catch (error) {
|
||||
console.error("Failed to update the report:", error);
|
||||
toast.error("Update failed");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Navigate back to the report list page
|
||||
*/
|
||||
const handleBack = () => {
|
||||
navigate("/admin/reportbuild2all");
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove a parameter from the selected parameters list
|
||||
* @param {number} index - Index of parameter to remove
|
||||
*/
|
||||
const handleRemoveParam = (index) => {
|
||||
const newParams = entryForm.std_param_html.filter((_, i) => i !== index);
|
||||
setEntryForm(prev => ({
|
||||
...prev,
|
||||
std_param_html: newParams,
|
||||
}));
|
||||
};
|
||||
|
||||
return (
|
||||
<Container
|
||||
className="mt-4 p-4"
|
||||
style={{
|
||||
maxWidth: "900px",
|
||||
backgroundColor: "#ffffff",
|
||||
borderRadius: "10px",
|
||||
boxShadow: "0 4px 10px rgba(0, 0, 0, 0.2)",
|
||||
border: "1px solid #ddd",
|
||||
marginBottom: "4rem"
|
||||
}}
|
||||
>
|
||||
{/* Header Section */}
|
||||
<h3 className="d-inline" style={{ fontWeight: 300 }}>
|
||||
<strong>REPORT SET UP - Project Details Report</strong>
|
||||
</h3>
|
||||
<Badge color="info" className="ms-3">
|
||||
Edit Mode
|
||||
</Badge>
|
||||
{reportId && (
|
||||
<Badge color="primary" className="ms-2">
|
||||
ID: {reportId}
|
||||
</Badge>
|
||||
)}
|
||||
<hr />
|
||||
|
||||
{/* Form Section */}
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<Row>
|
||||
{/* URL Input Field */}
|
||||
<Col md={6} sm={12} className="mb-4">
|
||||
<FormGroup>
|
||||
<Label htmlFor="url" className="fw-bold">
|
||||
Get URL
|
||||
</Label>
|
||||
<div className="d-flex align-items-center">
|
||||
<Input
|
||||
type="text"
|
||||
id="url"
|
||||
name="url"
|
||||
value={entryForm.url}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Enter URL"
|
||||
className="me-2"
|
||||
style={{ borderColor: "#0E6591" }}
|
||||
/>
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={() => getKeys(entryForm.url)}
|
||||
style={{ backgroundColor: "#0E6591", borderColor: "#0E6591" }}
|
||||
>
|
||||
<i className="bi bi-list"></i>
|
||||
</Button>
|
||||
</div>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
|
||||
{/* Checkbox for Date Filter */}
|
||||
<Col md={6} sm={12} className="mb-4">
|
||||
<FormGroup>
|
||||
<Label htmlFor="date_param_req" className="fw-bold">
|
||||
Include Date Filter
|
||||
</Label>
|
||||
<div className="d-flex align-items-center">
|
||||
<Input
|
||||
type="checkbox"
|
||||
id="date_param_req"
|
||||
name="date_param_req"
|
||||
checked={entryForm.date_param_req}
|
||||
onChange={handleInputChange}
|
||||
className="ms-2"
|
||||
/>
|
||||
</div>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
|
||||
{/* Standard Parameters Selection */}
|
||||
<Col md={6} sm={12} className="mb-4">
|
||||
<FormGroup>
|
||||
<Label className="fw-bold">Standard Parameters</Label>
|
||||
<div>
|
||||
|
||||
{/* Multiselect dropdown for parameters */}
|
||||
<Multiselect
|
||||
options={keysFromUrl.map(key => ({ name: key, id: key }))}
|
||||
selectedValues={entryForm.std_param_html.map(param => ({ name: param, id: param }))}
|
||||
onSelect={(selectedList) => handleMultiselectSelect(selectedList.map(item => item.id))}
|
||||
onRemove={(selectedList) => handleMultiselectRemove(selectedList.map(item => item.id))}
|
||||
displayValue="name"
|
||||
placeholder="Select Parameters"
|
||||
showCheckbox={true}
|
||||
style={{
|
||||
searchBox: {
|
||||
borderRadius: '0.25rem',
|
||||
border: '1px solid #0E6591'
|
||||
},
|
||||
chips: {
|
||||
background: "#0E6591"
|
||||
},
|
||||
optionContainer: {
|
||||
borderColor: "#0E6591"
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
|
||||
{/* List Dropdown */}
|
||||
<Col md={6} sm={12} className="mb-4">
|
||||
<FormGroup>
|
||||
<Label className="fw-bold">List</Label>
|
||||
<Input
|
||||
type="select"
|
||||
className="form-control"
|
||||
style={{ borderColor: "#0E6591" }}
|
||||
>
|
||||
<option value="">Choose from list</option>
|
||||
{databaseList.map((item, index) => (
|
||||
<option key={index} value={item.name}>
|
||||
{item.name}
|
||||
</option>
|
||||
))}
|
||||
</Input>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
{/* Action Buttons */}
|
||||
<div className="text-center mt-4">
|
||||
<Button
|
||||
color="secondary"
|
||||
outline
|
||||
className="me-3"
|
||||
onClick={handleBack}
|
||||
style={{
|
||||
borderColor: "#6c757d",
|
||||
color: "#6c757d",
|
||||
borderRadius: "20px",
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
BACK
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
color="primary"
|
||||
style={{
|
||||
backgroundColor: "#0E6591",
|
||||
borderColor: "#0E6591",
|
||||
borderRadius: "20px",
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
>
|
||||
UPDATE
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
export default ReportBuild2;
|
||||
|
||||
10
src/components/Dashboard/Reportbuild2/ReportBuild2Test.js
Normal file
10
src/components/Dashboard/Reportbuild2/ReportBuild2Test.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import ReportBuild2 from './ReportBuild2';
|
||||
|
||||
describe('ReportBuild2 Component', () => {
|
||||
it('should render without crashing', () => {
|
||||
const { getByText } = render(<ReportBuild2 />);
|
||||
expect(getByText('Report Build 2 Component')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
15
src/components/Dashboard/Reportbuild2/ReportBuilder2.js
Normal file
15
src/components/Dashboard/Reportbuild2/ReportBuilder2.js
Normal file
@@ -0,0 +1,15 @@
|
||||
import React, { useEffect } from 'react';
|
||||
|
||||
const ReportBuild2 = () => {
|
||||
useEffect(() => {
|
||||
// Component did mount equivalent
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="report-build2">
|
||||
<h1>Report Build 2 Component</h1>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ReportBuild2;
|
||||
1004
src/components/Dashboard/Reportbuilder/UserDetailsView.js
Normal file
1004
src/components/Dashboard/Reportbuilder/UserDetailsView.js
Normal file
File diff suppressed because it is too large
Load Diff
2148
src/components/Dashboard/Reportbuilder/reportQuery.js
Normal file
2148
src/components/Dashboard/Reportbuilder/reportQuery.js
Normal file
File diff suppressed because it is too large
Load Diff
203
src/components/Dashboard/SetupView.js
Normal file
203
src/components/Dashboard/SetupView.js
Normal file
@@ -0,0 +1,203 @@
|
||||
import React,{useState,useEffect} from "react";
|
||||
import "../Dashboard/CSS/CSS/SetupView.css"; // Assuming you have a CSS file for styling
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import Spinner from '../../UIComponants/Spinner';
|
||||
|
||||
function SetupView({
|
||||
onUserMaintenanceClick,
|
||||
onMenuAccessControl,
|
||||
onUserGroupMaintenance,
|
||||
onSystemParameter,
|
||||
onMenuMaintenance,
|
||||
onAccessType,
|
||||
onAPIregistry,
|
||||
onTokenregistry,
|
||||
onDataType1,
|
||||
onDataType2,
|
||||
onDataType3,
|
||||
onDataType4,
|
||||
onDataType5,
|
||||
onDataType6,
|
||||
onDynamicTable,
|
||||
oncodeExtension,
|
||||
}) {
|
||||
const navigate = useNavigate();
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate loading data
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
}, 3000); // Simulated 3 seconds loading
|
||||
}, []);
|
||||
|
||||
return (
|
||||
|
||||
<div className="setup-view">
|
||||
{loading ? (
|
||||
<Spinner /> // Display the spinner while loading
|
||||
) : (
|
||||
<div className="usercards-container">
|
||||
<div className="usercards">
|
||||
<div
|
||||
className="usercard"
|
||||
onClick={() => {
|
||||
console.log("User Maintenance card clicked");
|
||||
console.log("Navigating to:", "/admin/user-maintenance");
|
||||
|
||||
navigate("/admin/user-maintenance");
|
||||
}}
|
||||
// onClick={onUserMaintenanceClick}
|
||||
>
|
||||
<i className="fa fa-user-cog card-icon"></i>
|
||||
<h3>User Maintenance</h3>
|
||||
<p>Content for Card 1</p>
|
||||
</div>
|
||||
<div
|
||||
className="usercard"
|
||||
onClick={() => {
|
||||
navigate("/admin/menu-access-control");
|
||||
// navigate("/admin/menu-access-control2")
|
||||
}}
|
||||
>
|
||||
<i className="fa-solid fa-lock"></i>
|
||||
<h3>Menu Access Control</h3>
|
||||
<p>Content for Card 2</p>
|
||||
</div>
|
||||
<div className="usercard" onClick={() => {
|
||||
navigate("/admin/user-Group-Maintenance");
|
||||
}}>
|
||||
<i className="fa-solid fa-users"></i>
|
||||
<h3>User Group Maintenance</h3>
|
||||
<p>Content for Card 3</p>
|
||||
</div>
|
||||
<div className="usercard" onClick={() => {
|
||||
navigate("/admin/system-parameter");
|
||||
}}>
|
||||
<i className="fa-solid fa-gears"></i>
|
||||
<h3>System Parameter</h3>
|
||||
<p>Content for Card 4</p>
|
||||
</div>
|
||||
<div className="usercard" onClick={() => {
|
||||
navigate("/admin/menu-maintenance");
|
||||
}}>
|
||||
<i className="fa-solid fa-utensils"></i>
|
||||
<h3>Menu Maintenance</h3>
|
||||
<p>Content for Card 5</p>
|
||||
</div>
|
||||
<div className="usercard" onClick={() => {
|
||||
navigate("/admin/access-type");
|
||||
}}>
|
||||
<i className="fa-solid fa-key"></i>
|
||||
<h3>Access Type</h3>
|
||||
<p>Content for Card 6</p>
|
||||
</div>
|
||||
<div className="usercard" onClick={()=>{
|
||||
navigate("/admin/api-registry")
|
||||
}}>
|
||||
<i className="fas fa-database"></i>
|
||||
<h3>API Registry</h3>
|
||||
<p>Content for Card 7</p>
|
||||
</div>
|
||||
<div className="usercard" onClick={()=>{
|
||||
navigate("/admin/token-registry")
|
||||
}}>
|
||||
<i className="fas fa-key"></i>
|
||||
<h3>Token Registry</h3>
|
||||
<p>Content for Card 8</p>
|
||||
</div>
|
||||
{/* <div className="usercard" onClick={()=>{
|
||||
navigate("/admin/datatype-1")
|
||||
}}>
|
||||
<i className="fa fa-file"></i>
|
||||
<h3>DATATYPE1</h3>
|
||||
<p>Content for Card 9</p>
|
||||
</div>
|
||||
<div className="usercard" onClick={()=>{
|
||||
navigate("/admin/datatype-2")
|
||||
}}>
|
||||
<i className="fa fa-file"></i>
|
||||
<h3>DATATYPE2</h3>
|
||||
<p>Content for Card 10</p>
|
||||
</div>
|
||||
<div className="usercard" onClick={()=>{
|
||||
navigate("/admin/datatype-3")
|
||||
}}>
|
||||
<i className="fa fa-file"></i>
|
||||
<h3>DATATYPE3</h3>
|
||||
<p>Content for Card 11</p>
|
||||
</div>
|
||||
<div className="usercard" onClick={()=>{
|
||||
navigate("/admin/datatype-4")
|
||||
}}>
|
||||
<i className="fa fa-file"></i>
|
||||
<h3>DATATYPE4</h3>
|
||||
<p>Content for Card 12</p>
|
||||
</div>
|
||||
<div className="usercard"onClick={()=>{
|
||||
navigate("/admin/datatype-5")
|
||||
}}>
|
||||
<i className="fa fa-file"></i>
|
||||
<h3>DATATYPE5</h3>
|
||||
<p>Content for Card 13</p>
|
||||
</div>
|
||||
<div className="usercard" onClick={()=>{
|
||||
navigate("/admin/datatype-6")
|
||||
}}>
|
||||
<i className="fa fa-file"></i>
|
||||
<h3>DATATYPE6</h3>
|
||||
<p>Content for Card 14</p>
|
||||
</div>
|
||||
<div className="usercard" onClick={()=>{
|
||||
navigate("/admin/basics-datatypes")
|
||||
}}>
|
||||
<i className="fa fa-file"></i>
|
||||
<h3>Basics Datatypes</h3>
|
||||
<p>Content for Card 15</p>
|
||||
</div>
|
||||
<div className="usercard" onClick={()=>{
|
||||
navigate("/admin/advance-datatypes")
|
||||
}}>
|
||||
<i className="fa fa-file"></i>
|
||||
<h3>Advanced Datatypes</h3>
|
||||
<p>Content for Card 16</p>
|
||||
</div>
|
||||
<div className="usercard" onClick={()=>{
|
||||
navigate("/admin/advance-datatypes2")
|
||||
}}>
|
||||
<i className="fa fa-file"></i>
|
||||
<h3>Advanced Datatypes 2</h3>
|
||||
<p>Content for Card 17</p>
|
||||
</div>
|
||||
<div className="usercard" onClick={()=>{
|
||||
navigate("/admin/premium-datatypes")
|
||||
}}>
|
||||
<i className="fa fa-file"></i>
|
||||
<h3>Premium Datatypes</h3>
|
||||
<p>Content for Card 18</p>
|
||||
</div> */}
|
||||
<div className="usercard" onClick={()=>{
|
||||
navigate("/admin/user-report")
|
||||
}}>
|
||||
<i className="fa fa-file"></i>
|
||||
<h3>Reports</h3>
|
||||
<p>Report Description</p>
|
||||
</div>
|
||||
<div className="usercard" onClick={()=>{
|
||||
navigate("/admin/dynamic-form")
|
||||
}}>
|
||||
<i className="fa fa-file"></i>
|
||||
<h3>DynamicForm</h3>
|
||||
<p>Content for Card 16</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
export default SetupView;
|
||||
548
src/components/Dashboard/SubmenuMaintenance.js
Normal file
548
src/components/Dashboard/SubmenuMaintenance.js
Normal file
@@ -0,0 +1,548 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import Spinner from "../../UIComponants/Spinner";
|
||||
import "../Dashboard/CSS/CSS/MenuMaitenance.css";
|
||||
import { Table, Pagination, PaginationItem, PaginationLink } from "reactstrap";
|
||||
import { BsJournals } from "react-icons/bs";
|
||||
import {
|
||||
Button,
|
||||
Dropdown,
|
||||
Modal,
|
||||
Form,
|
||||
Row,
|
||||
Col,
|
||||
InputGroup,
|
||||
FormControl,
|
||||
OverlayTrigger,
|
||||
Tooltip,
|
||||
} from "react-bootstrap";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { FaSearch } from "react-icons/fa";
|
||||
import { faEdit, faTrashAlt } from "@fortawesome/free-solid-svg-icons";
|
||||
import { addSubmenuItem } from "../../APIServices/MenuMaintenanceAPI";
|
||||
import { toast } from "react-toastify";
|
||||
const SubMenuMaintenance = ({ selectedMainMenuId }) => {
|
||||
const [recordsPerPage, setRecordsPerPage] = useState(10);
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [subMenu, setSubMenu] = useState([]);
|
||||
const [modalAdd, setModalAdd] = useState(false);
|
||||
const [modalDelete, setModalDelete] = useState(false);
|
||||
const [modalEdit, setModalEdit] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [rowSelected, setRowSelected] = useState({});
|
||||
|
||||
const [entryForm, setEntryForm] = useState({
|
||||
menuId: selectedMainMenuId,
|
||||
menuItemDesc: "",
|
||||
itemSeq: "",
|
||||
moduleName: "",
|
||||
status: "Enable",
|
||||
main_menu_action_name: "",
|
||||
});
|
||||
const [data, setData] = useState([]);
|
||||
|
||||
const [visibleColumns, setVisibleColumns] = useState({
|
||||
menu_id: true,
|
||||
menuItemDesc: true,
|
||||
itemSeq: true,
|
||||
moduleName: true,
|
||||
main_menu_action_name: true,
|
||||
status: true,
|
||||
});
|
||||
|
||||
const [isSubMenu, setIsSubMenu] = useState(false);
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const [menuItems, setMenuItems] = useState([]);
|
||||
const [slicedSubMenus, setSlicedSubMenus] = useState([]);
|
||||
const [currentMenuItem, setCurrentMenuItem] = useState({});
|
||||
const [subMenuItems, setSubMenuItems] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate loading data
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
}, 3000); // Simulated 3 seconds loading
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedMainMenuId) {
|
||||
setEntryForm((prev) => ({ ...prev, menuId: selectedMainMenuId }));
|
||||
}
|
||||
}, [selectedMainMenuId]);
|
||||
|
||||
useEffect(() => {
|
||||
const filteredSubMenus = subMenu
|
||||
.filter(
|
||||
(submenu) =>
|
||||
submenu.menuItemDesc &&
|
||||
submenu.menuItemDesc.toLowerCase().includes(searchQuery.toLowerCase())
|
||||
)
|
||||
.slice((currentPage - 1) * recordsPerPage, currentPage * recordsPerPage);
|
||||
|
||||
setSlicedSubMenus(filteredSubMenus);
|
||||
}, [subMenu, searchQuery, currentPage, recordsPerPage]);
|
||||
|
||||
const handleInputChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
setEntryForm((prev) => ({ ...prev, [name]: value }));
|
||||
};
|
||||
|
||||
const handleRowChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
setRowSelected((prev) => ({ ...prev, [name]: value }));
|
||||
};
|
||||
|
||||
const handleSearch = (query) => {
|
||||
setSearchQuery(query);
|
||||
};
|
||||
|
||||
// const goToAdd = () => {
|
||||
// setEntryForm((prev) => ({
|
||||
// ...prev,
|
||||
// menuId: selectedMainMenuId, // Set the main menu ID
|
||||
// }));
|
||||
// setModalAdd(true);
|
||||
// };
|
||||
|
||||
const goToAdd = () => {
|
||||
console.log("selectedMainMenuId:", selectedMainMenuId); // Debugging line
|
||||
if (!selectedMainMenuId) {
|
||||
toast.error("Please select a main menu item first.");
|
||||
return;
|
||||
}
|
||||
setEntryForm((prev) => ({
|
||||
...prev,
|
||||
menuId: selectedMainMenuId,
|
||||
}));
|
||||
setModalAdd(true);
|
||||
};
|
||||
|
||||
|
||||
const onSubmit = async (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
// Include menuId in the form data
|
||||
const formData = {
|
||||
menuItemDesc: entryForm.menuItemDesc,
|
||||
};
|
||||
|
||||
try {
|
||||
// Pass menuId as a string
|
||||
const data = await addSubmenuItem(
|
||||
selectedMainMenuId.toString(),
|
||||
formData
|
||||
);
|
||||
toast.success("Submenu item added successfully!");
|
||||
console.log("Submenu item added successfully:", data);
|
||||
// Handle successful submission, e.g., close modal, refresh data, etc.
|
||||
setModalAdd(false);
|
||||
} catch (error) {
|
||||
console.error("Error adding submenu item:", error);
|
||||
toast.error(
|
||||
"Unexpected error: " +
|
||||
(error.message || "Server error occurred. Please try again.")
|
||||
);
|
||||
// Handle error, e.g., show a message to the user
|
||||
}
|
||||
};
|
||||
|
||||
const goToEdit = (user) => {
|
||||
setRowSelected(user);
|
||||
setEntryForm({
|
||||
menuId: user.menuId,
|
||||
menuItemDesc: user.menuItemDesc,
|
||||
});
|
||||
setModalEdit(true);
|
||||
};
|
||||
|
||||
const onUpdate = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
// Prepare the updated entry from the form
|
||||
const updatedEntry = {
|
||||
menuId: entryForm.menuId,
|
||||
menuItemDesc: entryForm.menuItemDesc,
|
||||
// Add other fields as needed
|
||||
};
|
||||
|
||||
// Find the index of the subMenu to update
|
||||
const index = slicedSubMenus.findIndex(
|
||||
(subMenu) => subMenu.menuId === updatedEntry.menuId
|
||||
);
|
||||
|
||||
if (index !== -1) {
|
||||
// Update the subMenu in the state
|
||||
const updatedSubMenus = [...slicedSubMenus];
|
||||
updatedSubMenus[index] = updatedEntry;
|
||||
|
||||
// Update the state with the new subMenus list
|
||||
setSlicedSubMenus(updatedSubMenus);
|
||||
|
||||
// Close the modal after updating
|
||||
setModalEdit(false);
|
||||
}
|
||||
};
|
||||
|
||||
const onDelete = (user) => {
|
||||
setRowSelected(user);
|
||||
setModalDelete(true);
|
||||
};
|
||||
|
||||
const confirmDelete = () => {
|
||||
// Logic to delete
|
||||
console.log("Deleted:", rowSelected);
|
||||
setModalDelete(false);
|
||||
};
|
||||
|
||||
// pagination
|
||||
const toggleColumn = (column) => {
|
||||
setVisibleColumns((prev) => ({
|
||||
...prev,
|
||||
[column]: !prev[column],
|
||||
}));
|
||||
};
|
||||
|
||||
const totalPages = Math.ceil(subMenu.length / recordsPerPage);
|
||||
const handlePageChange = (pageNumber) => {
|
||||
setCurrentPage(pageNumber);
|
||||
};
|
||||
const handleRecordsPerPageChange = (number) => {
|
||||
setRecordsPerPage(number);
|
||||
};
|
||||
return (
|
||||
<div style={{ marginTop: "11rem" }}>
|
||||
{loading ? (
|
||||
<Spinner />
|
||||
) : (
|
||||
<div className="submenu-maintenance">
|
||||
{/* Header */}
|
||||
<div className="row mt-3">
|
||||
<div className="col-md-8">
|
||||
<h3 className=" d-inline title_main">Sub-Menu Maintenance</h3>
|
||||
<span className="badge bg-primary ms-3">Sub Menu</span>
|
||||
</div>
|
||||
</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>
|
||||
<Col xs={12} md={4} lg={6} className="d-flex justify-content-end">
|
||||
<div className="col-md-4 text-end">
|
||||
<button className="btn btn-primary" onClick={goToAdd}>
|
||||
<i className="bi bi-plus"></i> ADD
|
||||
</button>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<div className="table-responsive">
|
||||
<Table
|
||||
striped
|
||||
responsive
|
||||
hover
|
||||
align="middle"
|
||||
className="table-flush shadow-sm"
|
||||
>
|
||||
<thead className="custom_header">
|
||||
<tr>
|
||||
<th>No</th>
|
||||
{Object.entries(visibleColumns).map(
|
||||
([key, visible]) =>
|
||||
visible && (
|
||||
<th key={key}>
|
||||
{key.charAt(0).toUpperCase() + key.slice(1)}
|
||||
</th>
|
||||
)
|
||||
)}
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="tbody">
|
||||
{slicedSubMenus.length === 0 ? (
|
||||
<tr>
|
||||
<td
|
||||
colSpan={
|
||||
1 + // For the "No" column
|
||||
Object.keys(visibleColumns).filter(
|
||||
(key) => visibleColumns[key]
|
||||
).length +
|
||||
(isSubMenu ? 1 : 2)
|
||||
}
|
||||
className="text-center"
|
||||
>
|
||||
No menu items found. Please add new items.
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
slicedSubMenus.map((subMenu, index) => (
|
||||
<tr key={index}>
|
||||
<td>{index + 1}</td>
|
||||
{Object.keys(visibleColumns).map(
|
||||
(key) =>
|
||||
visibleColumns[key] && (
|
||||
<td key={key}>
|
||||
{key === "status" ? (
|
||||
<span
|
||||
className="status"
|
||||
style={{
|
||||
color: subMenu.status ? "green" : "red",
|
||||
backgroundColor: subMenu.status
|
||||
? "#d4f7d4"
|
||||
: "#f7d4d4",
|
||||
padding: "4px 8px",
|
||||
borderRadius: "4px",
|
||||
display: "inline-block",
|
||||
}}
|
||||
>
|
||||
{subMenu.status ? "Enabled" : "Disabled"}
|
||||
</span>
|
||||
) : (
|
||||
subMenu[key]
|
||||
)}
|
||||
</td>
|
||||
)
|
||||
)}
|
||||
<td className="text-center">
|
||||
<FontAwesomeIcon
|
||||
icon={faEdit}
|
||||
onClick={() => goToEdit(subMenu)}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
fontSize: "1rem",
|
||||
color: "green",
|
||||
marginRight: "15px",
|
||||
}}
|
||||
/>
|
||||
<FontAwesomeIcon
|
||||
icon={faTrashAlt}
|
||||
onClick={() => onDelete(subMenu)}
|
||||
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>
|
||||
|
||||
{/* Modals */}
|
||||
{modalAdd && (
|
||||
<div className="modal fade show d-block" tabIndex="-1">
|
||||
<div className="modal-dialog">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header">
|
||||
<h5 className="modal-title">Add Sub-Menu</h5>
|
||||
<button
|
||||
type="button"
|
||||
className="btn-close"
|
||||
onClick={() => setModalAdd(false)}
|
||||
></button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<form onSubmit={onSubmit}>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control mb-2"
|
||||
name="menuId"
|
||||
placeholder="Menu ID"
|
||||
value={entryForm.menuId || selectedMainMenuId}
|
||||
onChange={handleInputChange}
|
||||
readOnly
|
||||
/>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
className="form-control mb-2"
|
||||
name="menuItemDesc"
|
||||
placeholder="Menu Item Name"
|
||||
value={entryForm.menuItemDesc}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
<button type="submit" className="btn btn-primary">
|
||||
Add
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{modalEdit && (
|
||||
<div className="modal fade show d-block" tabIndex="-1">
|
||||
<div className="modal-dialog">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header">
|
||||
<h5 className="modal-title">Edit Sub-Menu</h5>
|
||||
<button
|
||||
type="button"
|
||||
className="btn-close"
|
||||
onClick={() => setModalEdit(false)}
|
||||
></button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<form onSubmit={onUpdate}>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control mb-2"
|
||||
name="menuId"
|
||||
placeholder="Menu ID"
|
||||
value={entryForm.menuId}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control mb-2"
|
||||
name="menuItemDesc"
|
||||
placeholder="Menu Item Name"
|
||||
value={entryForm.menuItemDesc}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
<button type="submit" className="btn btn-primary">
|
||||
Update
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SubMenuMaintenance;
|
||||
354
src/components/Dashboard/SystemParameter.js
Normal file
354
src/components/Dashboard/SystemParameter.js
Normal file
@@ -0,0 +1,354 @@
|
||||
// import React, { useState } from "react";
|
||||
// import { Button, Form, Container, Row, Col } from "react-bootstrap";
|
||||
// import "bootstrap/dist/css/bootstrap.min.css";
|
||||
|
||||
// const SystemParameterForm = () => {
|
||||
// const [formData, setFormData] = useState({
|
||||
// schedulerTimer: "",
|
||||
// leaseTaxCode: "",
|
||||
// vesselConfirmationProcessLimit: "",
|
||||
|
||||
// rowToDisplay: "",
|
||||
// linkToDisplay: "",
|
||||
// rowToAdd: "",
|
||||
// lovRowToDisplay: "",
|
||||
// lovLinkToDisplay: "",
|
||||
// oldServerName: "",
|
||||
// oldBase: "",
|
||||
// oldAdminUser: "",
|
||||
// oldServerPort: "",
|
||||
// userDefaultGroup: "",
|
||||
// defaultDepartment: "",
|
||||
// defaultPosition: "",
|
||||
// singleCharge: "",
|
||||
// firstDayOfWeek: "",
|
||||
// hourPerShift: "",
|
||||
// cnBillingFrequency: "",
|
||||
// billingDepartmentCode: "",
|
||||
// basePriceList: "",
|
||||
// nonContainerServiceOrderAutoApprovalDeptCode: "",
|
||||
// ediMAESchedulerOnOff: "",
|
||||
// ediSchedulerOnOff: "",
|
||||
// logo: null,
|
||||
// companyDisplayName: "",
|
||||
// });
|
||||
|
||||
// const handleInputChange = (event) => {
|
||||
// const { name, value } = event.target;
|
||||
// setFormData((prevState) => ({
|
||||
// ...prevState,
|
||||
// [name]: value,
|
||||
// }));
|
||||
// };
|
||||
|
||||
// const handleFileChange = (event) => {
|
||||
// setFormData((prevState) => ({
|
||||
// ...prevState,
|
||||
// logo: event.target.files[0],
|
||||
// }));
|
||||
// };
|
||||
|
||||
// const handleSubmit = (event) => {
|
||||
// event.preventDefault();
|
||||
// alert("Form submitted successfully!");
|
||||
// console.log("Form Data:", formData);
|
||||
// };
|
||||
|
||||
// const handleClear = () => {
|
||||
// setFormData({
|
||||
// schedulerTimer: "",
|
||||
// leaseTaxCode: "",
|
||||
// vesselConfirmationProcessLimit: "",
|
||||
|
||||
// rowToDisplay: "",
|
||||
// linkToDisplay: "",
|
||||
// rowToAdd: "",
|
||||
// lovRowToDisplay: "",
|
||||
// lovLinkToDisplay: "",
|
||||
// oldServerName: "",
|
||||
// oldBase: "",
|
||||
// oldAdminUser: "",
|
||||
// oldServerPort: "",
|
||||
// userDefaultGroup: "",
|
||||
// defaultDepartment: "",
|
||||
// defaultPosition: "",
|
||||
// singleCharge: "",
|
||||
// firstDayOfWeek: "",
|
||||
// hourPerShift: "",
|
||||
// cnBillingFrequency: "",
|
||||
// billingDepartmentCode: "",
|
||||
// basePriceList: "",
|
||||
// nonContainerServiceOrderAutoApprovalDeptCode: "",
|
||||
// ediMAESchedulerOnOff: "",
|
||||
// ediSchedulerOnOff: "",
|
||||
// logo: null,
|
||||
// companyDisplayName: "",
|
||||
// });
|
||||
// };
|
||||
|
||||
// return (
|
||||
// <Container className="mt-5">
|
||||
// <Form onSubmit={handleSubmit}>
|
||||
// <Row className="mb-3">
|
||||
// <Col xs={6}>
|
||||
// <h5>Setup Code</h5>
|
||||
// </Col>
|
||||
// <Col xs={6}>
|
||||
// <h5>Value</h5>
|
||||
// </Col>
|
||||
// </Row>
|
||||
// {Object.keys(formData).map((key, index) =>
|
||||
// key !== "logo" ? (
|
||||
// <Row className="mb-3" key={index}>
|
||||
// <Col xs={6}>
|
||||
// <Form.Label>
|
||||
// {key
|
||||
// .split(/(?=[A-Z])/)
|
||||
// .join(" ")
|
||||
// .replace(/\b\w/g, (l) => l.toUpperCase())}
|
||||
// </Form.Label>
|
||||
// </Col>
|
||||
// <Col xs={6}>
|
||||
// <Form.Control
|
||||
// style={{ borderColor: '#343a40' }}
|
||||
// type="text"
|
||||
// name={key}
|
||||
// value={formData[key]}
|
||||
// onChange={handleInputChange}
|
||||
// />
|
||||
// </Col>
|
||||
// </Row>
|
||||
// ) : (
|
||||
// <Row className="mb-3" key={index}>
|
||||
// <Col xs={6}>
|
||||
// <Form.Label>Upload Logo</Form.Label>
|
||||
// </Col>
|
||||
// <Col xs={6}>
|
||||
// <Form.Control style={{ borderColor: '#343a40' }} type="file" onChange={handleFileChange} />
|
||||
// </Col>
|
||||
// </Row>
|
||||
// )
|
||||
// )}
|
||||
// <div className="d-flex justify-content-end">
|
||||
// <Button variant="secondary" type="submit" className="me-2">
|
||||
// Save
|
||||
// </Button>
|
||||
// <Button variant="secondary" onClick={handleClear}>
|
||||
// Clear
|
||||
// </Button>
|
||||
// </div>
|
||||
// </Form>
|
||||
// </Container>
|
||||
// );
|
||||
// };
|
||||
|
||||
// export default SystemParameterForm;
|
||||
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Button, Form, Container, Row, Col } from "react-bootstrap";
|
||||
import "bootstrap/dist/css/bootstrap.min.css";
|
||||
import "../Dashboard/CSS/CSS/CommonStyle.css";
|
||||
import { toast } from "react-toastify";
|
||||
import Spinner from "../../UIComponants/Spinner";
|
||||
import {getSysParameter,addSysParameter} from "../../APIServices/SystemparameterApi";
|
||||
|
||||
const SystemParameterForm = () => {
|
||||
const [formData, setFormData] = useState({
|
||||
schedulerTimer: "",
|
||||
leaseTaxCode: "",
|
||||
vesselConfirmationProcessLimit: "",
|
||||
rowToDisplay: "",
|
||||
linkToDisplay: "",
|
||||
rowToAdd: "",
|
||||
lovRowToDisplay: "",
|
||||
lovLinkToDisplay: "",
|
||||
oldServerName: "",
|
||||
oldBase: "",
|
||||
oldAdminUser: "",
|
||||
oldServerPort: "",
|
||||
userDefaultGroup: "",
|
||||
defaultDepartment: "",
|
||||
defaultPosition: "",
|
||||
singleCharge: "",
|
||||
firstDayOfWeek: "",
|
||||
hourPerShift: "",
|
||||
cnBillingFrequency: "",
|
||||
billingDepartmentCode: "",
|
||||
basePriceList: "",
|
||||
nonContainerServiceOrderAutoApprovalDeptCode: "",
|
||||
ediMAESchedulerOnOff: "",
|
||||
ediSchedulerOnOff: "",
|
||||
logo: null,
|
||||
companyDisplayName: "",
|
||||
});
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate loading data
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
}, 3000); // Simulated 3 seconds loading
|
||||
}, []);
|
||||
const handleInputChange = (event) => {
|
||||
const { name, value } = event.target;
|
||||
setFormData((prevState) => ({
|
||||
...prevState,
|
||||
[name]: value,
|
||||
}));
|
||||
};
|
||||
|
||||
const handleFileChange = (event) => {
|
||||
setFormData((prevState) => ({
|
||||
...prevState,
|
||||
logo: event.target.files[0],
|
||||
}));
|
||||
};
|
||||
|
||||
// const handleSubmit = (event) => {
|
||||
// event.preventDefault();
|
||||
|
||||
// toast.success("Form submitted successfully!");
|
||||
// // alert("Form submitted successfully!");
|
||||
// console.log("Form Data:", formData);
|
||||
// };
|
||||
|
||||
const handleSubmit = async (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
try {
|
||||
const sysParamData = await addSysParameter(formData);
|
||||
console.log("API Response:", sysParamData);
|
||||
|
||||
toast.success("Form submitted successfully!");
|
||||
} catch (error) {
|
||||
console.error("Error:", error.response ? error.response.data : error.message);
|
||||
toast.error("Failed to submit the form. Please try again.");
|
||||
}
|
||||
};
|
||||
|
||||
const handleClear = () => {
|
||||
setFormData({
|
||||
schedulerTimer: "",
|
||||
leaseTaxCode: "",
|
||||
vesselConfirmationProcessLimit: "",
|
||||
|
||||
rowToDisplay: "",
|
||||
linkToDisplay: "",
|
||||
rowToAdd: "",
|
||||
lovRowToDisplay: "",
|
||||
lovLinkToDisplay: "",
|
||||
oldServerName: "",
|
||||
oldBase: "",
|
||||
oldAdminUser: "",
|
||||
oldServerPort: "",
|
||||
userDefaultGroup: "",
|
||||
defaultDepartment: "",
|
||||
defaultPosition: "",
|
||||
singleCharge: "",
|
||||
firstDayOfWeek: "",
|
||||
hourPerShift: "",
|
||||
cnBillingFrequency: "",
|
||||
billingDepartmentCode: "",
|
||||
basePriceList: "",
|
||||
nonContainerServiceOrderAutoApprovalDeptCode: "",
|
||||
ediMAESchedulerOnOff: "",
|
||||
ediSchedulerOnOff: "",
|
||||
logo: null,
|
||||
companyDisplayName: "",
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
marginTop: "10rem",
|
||||
marginBottom: "2rem",
|
||||
boxSizing: "border-box",
|
||||
}}
|
||||
>
|
||||
{loading ? (
|
||||
<Spinner />
|
||||
) : (
|
||||
<Container
|
||||
className="system_parameter mt-5 p-4 bg-light shadow-sm rounded"
|
||||
|
||||
>
|
||||
<h2
|
||||
className="title_main text-center mb-4"
|
||||
style={{ color: "#0E6591", fontWeight: "bold" }}
|
||||
>
|
||||
System Parameter Settings
|
||||
</h2>
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<Row className=" mb-3">
|
||||
<Col xs={6}>
|
||||
<h6 className="heading_main text-secondary">Setup Code</h6>
|
||||
</Col>
|
||||
<Col xs={6}>
|
||||
<h6 className="heading_main text-secondary">Value</h6>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
{Object.keys(formData).map((key, index) =>
|
||||
key !== "logo" ? (
|
||||
<Row className="mb-3" key={index}>
|
||||
<Col xs={6} className="d-flex align-items-center">
|
||||
<Form.Label className="mb-0">
|
||||
{key
|
||||
.split(/(?=[A-Z])/)
|
||||
.join(" ")
|
||||
.replace(/\b\w/g, (l) => l.toUpperCase())}
|
||||
</Form.Label>
|
||||
</Col>
|
||||
<Col xs={6}>
|
||||
<Form.Control
|
||||
className="p-2 custom-hover-border"
|
||||
style={{ borderColor: "#ced4da" }}
|
||||
type="text"
|
||||
name={key}
|
||||
value={formData[key]}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
) : (
|
||||
<Row className="mb-3" key={index}>
|
||||
<Col xs={6} className="d-flex align-items-center">
|
||||
<Form.Label className="mb-0">Upload Logo</Form.Label>
|
||||
</Col>
|
||||
<Col xs={6}>
|
||||
<Form.Control
|
||||
className="p-2 "
|
||||
style={{ borderColor: "#ced4da" }}
|
||||
type="file"
|
||||
onChange={handleFileChange}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
)
|
||||
)}
|
||||
|
||||
<div className="d-flex justify-content-end mt-4">
|
||||
<Button
|
||||
variant="primary"
|
||||
type="submit"
|
||||
className="me-2 px-4 py-2 custom_button"
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={handleClear}
|
||||
className="px-4 py-2 custom_button"
|
||||
>
|
||||
Clear
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</Container>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SystemParameterForm;
|
||||
462
src/components/Dashboard/TOKENRegistry.js
Normal file
462
src/components/Dashboard/TOKENRegistry.js
Normal file
@@ -0,0 +1,462 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Button, Dropdown,Modal, Form ,Row,Col,InputGroup,FormControl} from "react-bootstrap";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faEdit, faTrashAlt, faPlus,faBars,faTimes } from "@fortawesome/free-solid-svg-icons";
|
||||
import "bootstrap/dist/css/bootstrap.min.css";
|
||||
import "../Dashboard/CSS/CSS/CommonStyle.css";
|
||||
import { Table, Pagination, PaginationItem, PaginationLink } from "reactstrap";
|
||||
import { FaSearch, FaTimes } from "react-icons/fa";
|
||||
import { BsJournals } from "react-icons/bs";
|
||||
import { toast } from "react-toastify";
|
||||
import Spinner from '../../UIComponants/Spinner';
|
||||
|
||||
|
||||
function TOKENRegistry() {
|
||||
const initialTokens = JSON.parse(localStorage.getItem("tokens")) || [];
|
||||
const [tokens, setTokens] = useState(initialTokens);
|
||||
const [showAddEditModal, setShowAddEditModal] = useState(false);
|
||||
const [currentToken, setCurrentToken] = useState({
|
||||
tokenId: "",
|
||||
tokenName: "",
|
||||
tokenValue: "",
|
||||
isActive: true
|
||||
});
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const [recordsPerPage, setRecordsPerPage] = useState(10);
|
||||
const [visibleColumns, setVisibleColumns] = useState({
|
||||
tokenId: true,
|
||||
tokenName: true,
|
||||
tokenValue: true,
|
||||
isActive: true,
|
||||
actions: true
|
||||
});
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
const apiUrl = `${process.env.REACT_APP_API_URL}/apiregistery/getall`;
|
||||
const token = localStorage.getItem("authToken");
|
||||
|
||||
const fetchTokens = 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();
|
||||
setTokens(data);
|
||||
localStorage.setItem("tokens", JSON.stringify(data)); // Store tokens in local storage
|
||||
} catch (error) {
|
||||
console.error("Error fetching tokens:", error);
|
||||
}
|
||||
};
|
||||
|
||||
fetchTokens();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate loading data
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
}, 3000); // Simulated 3 seconds loading
|
||||
}, []);
|
||||
|
||||
const toggleColumn = (column) => {
|
||||
setVisibleColumns(prev => ({
|
||||
...prev,
|
||||
[column]: !prev[column],
|
||||
}));
|
||||
};
|
||||
|
||||
const handleInputChange = (event) => {
|
||||
const { name, value, type, checked } = event.target;
|
||||
setCurrentToken(prev => ({
|
||||
...prev,
|
||||
[name]: type === "checkbox" ? checked : value
|
||||
}));
|
||||
};
|
||||
|
||||
const handleSearch = (query) => {
|
||||
setSearchQuery(query);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setShowAddEditModal(false); // Close the modal by setting the state to false
|
||||
};
|
||||
|
||||
const handleRecordsPerPageChange = (number) => {
|
||||
setRecordsPerPage(number);
|
||||
setCurrentPage(1);
|
||||
};
|
||||
|
||||
const totalPages = Math.ceil(tokens.length / recordsPerPage);
|
||||
const handlePageChange = (pageNumber) => {
|
||||
setCurrentPage(pageNumber);
|
||||
};
|
||||
|
||||
const slicedTokens = tokens
|
||||
.filter(
|
||||
(item) =>
|
||||
item.tokenName &&
|
||||
item.tokenName.toLowerCase().includes(searchQuery.toLowerCase())
|
||||
)
|
||||
.slice((currentPage - 1) * recordsPerPage, currentPage * recordsPerPage);
|
||||
|
||||
|
||||
|
||||
|
||||
const handleSubmit = (event) => {
|
||||
event.preventDefault();
|
||||
try {
|
||||
if (isEditing) {
|
||||
setTokens(tokens.map(token =>
|
||||
token.tokenId === currentToken.tokenId ? currentToken : token
|
||||
));
|
||||
toast.success("Token updated successfully!");
|
||||
} else {
|
||||
const newTokenId = `ID${tokens.length + 1}`;
|
||||
setTokens([...tokens, { ...currentToken, tokenId: newTokenId }]);
|
||||
toast.success("Token added successfully!");
|
||||
}
|
||||
setShowAddEditModal(false);
|
||||
|
||||
localStorage.setItem("tokens", JSON.stringify(tokens)); // Update local storage with new tokens
|
||||
} catch (error) {
|
||||
toast.error("There was an error while submitting the token.");
|
||||
console.error("Error in handleSubmit:", error); // Log the error for debugging
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const openModal = (token = { tokenId: "", tokenName: "", tokenValue: "", isActive: false }) => {
|
||||
setIsEditing(!!token.tokenId);
|
||||
setCurrentToken(token);
|
||||
setShowAddEditModal(true);
|
||||
};
|
||||
|
||||
const handleDelete = (tokenId) => {
|
||||
setTokens(tokens.filter(token => token.tokenId !== tokenId));
|
||||
localStorage.setItem("tokens", JSON.stringify(tokens.filter(token => token.tokenId !== tokenId))); // Update local storage after deletion
|
||||
toast.success('Token deleted ...');
|
||||
};
|
||||
|
||||
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">Token Registry</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">
|
||||
<>
|
||||
{/* Add Icon */}
|
||||
<FontAwesomeIcon
|
||||
icon={faPlus}
|
||||
onClick={() => openModal(true)}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
fontSize: "1.5rem",
|
||||
color: "#747264",
|
||||
marginRight: "20px",
|
||||
}}
|
||||
/>
|
||||
<FontAwesomeIcon
|
||||
icon={faBars}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
fontSize: "1.5rem",
|
||||
color: "#747264",
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
|
||||
<div className="table-responsive">
|
||||
<Table striped responsive hover align="middle" className=" table-flush shadow-sm">
|
||||
<thead className="custom_header">
|
||||
<tr>
|
||||
{Object.keys(visibleColumns).filter(key => visibleColumns[key]).map(key => (
|
||||
<th key={key}>{key.charAt(0).toUpperCase() + key.slice(1)}</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="tbody">
|
||||
{slicedTokens.length === 0 ? (
|
||||
<tr>
|
||||
<td
|
||||
colSpan={
|
||||
Object.keys(visibleColumns).filter(
|
||||
(key) => visibleColumns[key]
|
||||
).length
|
||||
}
|
||||
className="text-center"
|
||||
>
|
||||
No Data Available
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
slicedTokens.map((token, index) => (
|
||||
<tr key={index}>
|
||||
{Object.keys(visibleColumns).filter(key => visibleColumns[key]).map(key => (
|
||||
<td key={key}>
|
||||
{key === "actions" ? (
|
||||
<>
|
||||
<FontAwesomeIcon
|
||||
icon={faEdit}
|
||||
onClick={() => openModal(token)}
|
||||
className="me-2"
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
fontSize: "1rem",
|
||||
color: "green",
|
||||
marginRight: "15px",
|
||||
}}
|
||||
/>
|
||||
<FontAwesomeIcon
|
||||
icon={faTrashAlt}
|
||||
onClick={() => handleDelete(token.tokenId)}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
fontSize: "1rem",
|
||||
color: "#dc3545",
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
) : key === "isActive" ? (
|
||||
<span
|
||||
className="status"
|
||||
style={{
|
||||
color: token.isActive ? "green" : "red",
|
||||
backgroundColor: token.isActive
|
||||
? "#d4f7d4"
|
||||
: "#f7d4d4",
|
||||
padding: "4px 8px",
|
||||
borderRadius: "4px",
|
||||
display: "inline-block",
|
||||
}}
|
||||
>
|
||||
{token.isActive ? "Active" : "Inactive"}
|
||||
</span>
|
||||
) : (
|
||||
token[key]
|
||||
)}
|
||||
</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>
|
||||
|
||||
|
||||
|
||||
<Modal show={showAddEditModal} onHide={() => setShowAddEditModal(false)}>
|
||||
<Modal.Header >
|
||||
<Modal.Title>{isEditing ? "Edit Token" : "Add Token"}</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="formTokenName">
|
||||
<Form.Label>Token Name</Form.Label>
|
||||
<Form.Control
|
||||
type="text"
|
||||
name="tokenName"
|
||||
value={currentToken.tokenName}
|
||||
onChange={handleInputChange}
|
||||
required
|
||||
className="custom-hover-border"
|
||||
/>
|
||||
</Form.Group>
|
||||
<Form.Group controlId="formTokenValue">
|
||||
<Form.Label>Token Value</Form.Label>
|
||||
<Form.Control
|
||||
type="text"
|
||||
name="tokenValue"
|
||||
value={currentToken.tokenValue}
|
||||
onChange={handleInputChange}
|
||||
required
|
||||
className="custom-hover-border"
|
||||
/>
|
||||
</Form.Group>
|
||||
<Form.Group controlId="formActive">
|
||||
<Form.Check
|
||||
type="checkbox"
|
||||
label="Active?"
|
||||
name="isActive"
|
||||
checked={currentToken.isActive}
|
||||
onChange={handleInputChange}
|
||||
className="custom-checkbox"
|
||||
/>
|
||||
</Form.Group>
|
||||
<Modal.Footer>
|
||||
<Button variant="primary" className="custom_button px-4" onClick={() => setShowAddEditModal(false)}>Close</Button>
|
||||
<Button variant="primary" className="custom_button px-4" type="submit">{isEditing ? "Update Token" : "Add Token"}</Button>
|
||||
</Modal.Footer>
|
||||
</Form>
|
||||
</Modal.Body>
|
||||
</Modal>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
export default TOKENRegistry;
|
||||
768
src/components/Dashboard/UserGroupMaintenance.js
Normal file
768
src/components/Dashboard/UserGroupMaintenance.js
Normal file
@@ -0,0 +1,768 @@
|
||||
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;
|
||||
1003
src/components/Dashboard/UserMaintenanceView.js
Normal file
1003
src/components/Dashboard/UserMaintenanceView.js
Normal file
File diff suppressed because it is too large
Load Diff
9
src/components/Dashboard/ValidationRule.js
Normal file
9
src/components/Dashboard/ValidationRule.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import React from 'react'
|
||||
|
||||
const ValidationRule = () => {
|
||||
return (
|
||||
<div>ValidationRule</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ValidationRule
|
||||
289
src/components/Dashboard/dashboardRunner/dashboardRunner.js
Normal file
289
src/components/Dashboard/dashboardRunner/dashboardRunner.js
Normal file
@@ -0,0 +1,289 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom'; // Add useNavigate
|
||||
import { Box, Typography, CircularProgress, Card, CardHeader, CardContent, Button } from '@mui/material';
|
||||
import DashboardBuilderService from "../../../APIServices/DashboardBuilderService";
|
||||
|
||||
// Import chart components
|
||||
import LineChartComponent from '../dashboardnew/gadgets/line-chart/LineChart';
|
||||
import PieChartComponent from '../dashboardnew/gadgets/pie-chart/Piechart';
|
||||
import PolarChartComponent from '../dashboardnew/gadgets/polar-chart/PolarChart';
|
||||
import RadarChartComponent from '../dashboardnew/gadgets/radar-chart/RadarChart';
|
||||
import BubbleChart from '../dashboardnew/gadgets/bubble-chart/BubbleChart';
|
||||
import BarChart from '../dashboardnew/gadgets/bar-chart/BarChart';
|
||||
import DoughnutChart from '../dashboardnew/gadgets/doughnut-charts/DoughnutChart';
|
||||
import DynamicChart from '../dashboardnew/gadgets/dynamic-chart/DynamicChart';
|
||||
import FinancialChart from '../dashboardnew/gadgets/financial-chart/FinancialChart';
|
||||
import GridViewComponent from '../dashboardnew/gadgets/grid-view/GridView';
|
||||
import ScatterChartComponent from '../dashboardnew/gadgets/scatter-chart/ScatterChart';
|
||||
import ToDoChartComponent from '../dashboardnew/gadgets/to-do-chart/TodoChart';
|
||||
|
||||
const ViewDashboard = () => {
|
||||
const { id } = useParams(); // Get dashboard ID from URL
|
||||
const navigate = useNavigate(); // Use the useNavigate hook
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
const [dashboard, setDashboard] = useState(null);
|
||||
const [layout, setLayout] = useState([]);
|
||||
|
||||
// Define component mapping object
|
||||
const componentMap = {
|
||||
'radar_chart': RadarChartComponent,
|
||||
'polar_chart': PolarChartComponent,
|
||||
'pie_chart': PieChartComponent,
|
||||
'bar_chart': BarChart,
|
||||
'bubble_chart': BubbleChart,
|
||||
'line_chart': LineChartComponent,
|
||||
'doughnut_chart': DoughnutChart,
|
||||
'dynamic_chart': DynamicChart,
|
||||
'financial_chart': FinancialChart,
|
||||
'grid_view': GridViewComponent,
|
||||
'scatter_chart': ScatterChartComponent,
|
||||
'todo_chart': ToDoChartComponent
|
||||
};
|
||||
|
||||
// Component name to identifier mapping for reverse lookup
|
||||
const componentNameToIdentifier = {
|
||||
'Radar Chart': 'radar_chart',
|
||||
'Polar Chart': 'polar_chart',
|
||||
'Pie Chart': 'pie_chart',
|
||||
'Bar Chart': 'bar_chart',
|
||||
'Bubble Chart': 'bubble_chart',
|
||||
'Line Chart': 'line_chart',
|
||||
'Doughnut Chart': 'doughnut_chart',
|
||||
'Dynamic Chart': 'dynamic_chart',
|
||||
'Financial Chart': 'financial_chart',
|
||||
'Grid View': 'grid_view',
|
||||
'Scatter Chart': 'scatter_chart',
|
||||
'Todo Chart': 'todo_chart'
|
||||
};
|
||||
|
||||
// Fetch dashboard data using the ID
|
||||
useEffect(() => {
|
||||
const fetchDashboard = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const response = await DashboardBuilderService.getById(id);
|
||||
console.log("Dashboard data:", response);
|
||||
|
||||
setDashboard({
|
||||
name: response.dashboard_name,
|
||||
dashboardLine: response.dashbord1_Line
|
||||
});
|
||||
|
||||
// Parse and transform widget data
|
||||
if (response.dashbord1_Line && response.dashbord1_Line[0]?.model) {
|
||||
try {
|
||||
const modelData = response.dashbord1_Line[0].model;
|
||||
const parsedModel = JSON.parse(modelData);
|
||||
|
||||
if (Array.isArray(parsedModel.dashboard)) {
|
||||
console.log("Dashboard widgets from API:", parsedModel.dashboard);
|
||||
|
||||
// Create the layout directly from the API response
|
||||
const dashboardLayout = parsedModel.dashboard.map((widget, index) => {
|
||||
// Get the component identifier from name
|
||||
const componentName = widget.component || "Unknown";
|
||||
const typeIdentifier = componentNameToIdentifier[componentName] ||
|
||||
componentName.toLowerCase().replace(' ', '_');
|
||||
|
||||
// Get component reference
|
||||
const Component = componentMap[typeIdentifier] || null;
|
||||
|
||||
return {
|
||||
id: `widget-${index}`,
|
||||
x: widget.x || 0,
|
||||
y: widget.y || 0,
|
||||
w: widget.cols || 6,
|
||||
h: widget.rows || 8,
|
||||
name: widget.name || componentName,
|
||||
component: Component,
|
||||
chartTitle: widget.chartTitle || widget.name || componentName,
|
||||
showLegend: widget.showLegend !== undefined ? widget.showLegend : true,
|
||||
showLabel: widget.showLabel !== undefined ? widget.showLabel : true,
|
||||
xAxis: widget.xAxis || 'Month',
|
||||
yAxis: widget.yAxis || 'Value'
|
||||
};
|
||||
});
|
||||
|
||||
console.log("Processed layout:", dashboardLayout);
|
||||
setLayout(dashboardLayout);
|
||||
} else {
|
||||
console.warn("Dashboard data is not an array:", parsedModel.dashboard);
|
||||
setLayout([]);
|
||||
}
|
||||
} catch (parseError) {
|
||||
console.error("Error parsing dashboard model:", parseError);
|
||||
setError("Failed to parse dashboard data. The dashboard may be corrupted.");
|
||||
setLayout([]);
|
||||
}
|
||||
} else {
|
||||
console.warn("No dashboard model found");
|
||||
setLayout([]);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching dashboard:", error);
|
||||
setError("Failed to load dashboard. Please try again later.");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (id) {
|
||||
fetchDashboard();
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
// Calculate absolute positioning based on grid coordinates
|
||||
const getWidgetStyle = (widget) => {
|
||||
const columnWidth = 100 / 6; // 12-column grid, width percentage
|
||||
const rowHeight = 40; // Fixed row height in pixels
|
||||
|
||||
return {
|
||||
position: 'absolute',
|
||||
left: `${widget.x * columnWidth}%`,
|
||||
top: `${widget.y * rowHeight}px`,
|
||||
width: `${widget.w * columnWidth}%`,
|
||||
height: `${widget.h * rowHeight}px`,
|
||||
};
|
||||
};
|
||||
|
||||
// Render loading state
|
||||
if (loading) {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
height: '100vh'
|
||||
}}
|
||||
>
|
||||
<CircularProgress />
|
||||
<Typography variant="h6" sx={{ ml: 2 }}>
|
||||
Loading Dashboard...
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
// Render error state
|
||||
if (error) {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
height: '100vh',
|
||||
flexDirection: 'column'
|
||||
}}
|
||||
>
|
||||
<Typography variant="h5" color="error" gutterBottom>
|
||||
Error Loading Dashboard
|
||||
</Typography>
|
||||
<Typography variant="body1">{error}</Typography>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box sx={{ padding: 0 }}>
|
||||
{/* Dashboard Header */}
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<h2>{dashboard?.name || 'Dashboard View'}</h2>
|
||||
<Button variant="contained" onClick={() => navigate(-1)}>Back</Button>
|
||||
</Box>
|
||||
|
||||
|
||||
{/* Dashboard Content */}
|
||||
{layout.length === 0 ? (
|
||||
<Box sx={{ textAlign: 'center', p: 5 }}>
|
||||
<Typography variant="h6">No widgets found in this dashboard.</Typography>
|
||||
</Box>
|
||||
) : (
|
||||
<Box
|
||||
sx={{
|
||||
position: 'relative',
|
||||
minHeight: '800px' // Set a minimum height for the dashboard area
|
||||
}}
|
||||
>
|
||||
{layout.map((widget) => {
|
||||
const Component = widget.component;
|
||||
const style = getWidgetStyle(widget);
|
||||
|
||||
return (
|
||||
<Box
|
||||
key={widget.id}
|
||||
sx={{
|
||||
...style,
|
||||
padding: 1,
|
||||
boxSizing: 'border-box'
|
||||
}}
|
||||
>
|
||||
<Card sx={{
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
boxShadow: '0 2px 10px rgba(0, 0, 0, 0.08)'
|
||||
}}>
|
||||
<CardHeader
|
||||
title={widget.chartTitle}
|
||||
titleTypographyProps={{ variant: 'h6' }}
|
||||
sx={{
|
||||
p: 2,
|
||||
pb: 1,
|
||||
backgroundColor: '#fafafa',
|
||||
borderBottom: '1px solid #eee'
|
||||
}}
|
||||
/>
|
||||
<CardContent sx={{
|
||||
flex: 1,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
p: 2,
|
||||
pt: 1,
|
||||
overflow: 'hidden'
|
||||
}}>
|
||||
{Component ? (
|
||||
<Box sx={{
|
||||
flexGrow: 1,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}>
|
||||
<Component
|
||||
chartTitle={widget.chartTitle}
|
||||
showLegend={widget.showLegend}
|
||||
showLabel={widget.showLabel}
|
||||
xAxis={widget.xAxis}
|
||||
yAxis={widget.yAxis}
|
||||
width="100%"
|
||||
height="100%"
|
||||
/>
|
||||
</Box>
|
||||
) : (
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
height: '100%'
|
||||
}}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
Widget type "{widget.name}" not available
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default ViewDashboard;
|
||||
@@ -0,0 +1,11 @@
|
||||
.card:hover {
|
||||
transform: scale(1.02);
|
||||
transition: transform 0.2s ease-in-out;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.205);
|
||||
background-color: 1px solid rgba(255, 255, 255, 0.062);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import moment from "moment";
|
||||
import { Toaster, toast } from "react-hot-toast";
|
||||
|
||||
import DashboardBuilderService from "../../../../APIServices/DashboardBuilderService";
|
||||
import "./dashboardRunnerAll.css";
|
||||
|
||||
|
||||
const DashboardRunnerAll = () => {
|
||||
const [addModal, setAddModal] = useState(false);
|
||||
const [data, setData] = useState([]);
|
||||
const [rows, setRows] = useState([]);
|
||||
const [rowSelected, setRowSelected] = useState(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
const [projectName, setProjectName] = useState("");
|
||||
const [projectId, setProjectId] = useState(null);
|
||||
const [moduleId, setModuleId] = useState(null);
|
||||
const [modalDelete, setModalDelete] = useState(false);
|
||||
|
||||
const navigate = useNavigate();
|
||||
const { id } = useParams();
|
||||
|
||||
useEffect(() => {
|
||||
setProjectId(sessionStorage.getItem("projectId"));
|
||||
setModuleId(sessionStorage.getItem("moduleId"));
|
||||
fetchDashboard();
|
||||
fetchProjectName(sessionStorage.getItem("projectId"));
|
||||
}, []);
|
||||
|
||||
const fetchProjectName = (id) => {
|
||||
// ModuleSetupService.getProjectModules(id).then((response) => {
|
||||
// setProjectName(response.items[0]?.projectName || "");
|
||||
// });
|
||||
};
|
||||
|
||||
const fetchDashboard = () => {
|
||||
setLoading(true);
|
||||
DashboardBuilderService.getAllDash()
|
||||
.then((response) => {
|
||||
setData(response);
|
||||
setRows(response);
|
||||
})
|
||||
.catch(() => setError("No data available"))
|
||||
.finally(() => setLoading(false));
|
||||
};
|
||||
|
||||
const handleDelete = (id) => {
|
||||
setModalDelete(false);
|
||||
// Dashboard3Service.deleteField(id).then(() => {
|
||||
// fetchDashboard();
|
||||
// toast.success("Deleted successfully");
|
||||
// });
|
||||
};
|
||||
|
||||
const handleExport = () => {
|
||||
// ExcelService.exportAsExcelFile(
|
||||
// rows,
|
||||
// "user_",
|
||||
// moment().format("YYYYMMDD_HHmmss")
|
||||
// );
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
navigate("/admin/dashboard-new-all");
|
||||
};
|
||||
|
||||
const handleEdit = (id) => {
|
||||
navigate(`../dashrunner/${id}`);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="dashboard-runner-all">
|
||||
<div className="row">
|
||||
<div className="col-8">
|
||||
<h3>All Dashboards</h3>
|
||||
</div>
|
||||
<div className="col-4 text-right">
|
||||
<button className="btn btn-primary" onClick={handleAdd}>
|
||||
<i className="fa fa-plus" /> Dashboard Builder
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{loading && (
|
||||
<div className="alert alert-info">
|
||||
<i className="fa fa-spinner fa-spin"></i> Loading...
|
||||
</div>
|
||||
)}
|
||||
|
||||
{error && <div className="alert alert-danger">{error}</div>}
|
||||
|
||||
{/* {!loading && data && (
|
||||
<div className="row">
|
||||
{data.map((app, index) => (
|
||||
<div key={index} className="col-auto">
|
||||
<div className="card" onClick={() => handleEdit(app.id)}>
|
||||
<div className="card-header">
|
||||
<i className="fa fa-th" />
|
||||
<b title={app.dashboard_name}>{app.dashboard_name}</b>
|
||||
<p title={app.description}>{app.description}</p>
|
||||
</div>
|
||||
<div className="card-footer">
|
||||
<button className="btn btn-sm btn-link">
|
||||
Last Updated: {moment(app.updatedAt).format("DD/MM/YYYY")}
|
||||
</button>
|
||||
<span>
|
||||
<i className="fa fa-calendar" /> {moment(app.createdAt).format("HH:mm:ss")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)} */}
|
||||
|
||||
{!loading && data && (
|
||||
<div className="row g-4 mt-1">
|
||||
{data.map((app, index) => (
|
||||
<div key={index} className="col-md-4 col-sm-6">
|
||||
<div
|
||||
className="card h-100 shadow-md"
|
||||
onClick={() => handleEdit(app.id)}
|
||||
style={{ cursor: "pointer", borderRadius: "8px" }}
|
||||
>
|
||||
<div className="card-header text-black d-flex align-items-center justify-content-between" style={{ fontSize:"1rem"}}>
|
||||
<span>
|
||||
<i className="fa fa-th me-2"></i><span> </span>
|
||||
<b title={app.dashboard_name} className="text-truncate">
|
||||
{app.dashboard_name}
|
||||
</b>
|
||||
</span>
|
||||
</div>
|
||||
<div className="card-body">
|
||||
<p title={app.description} className="text-muted mb-2 text-truncate">
|
||||
{app.description || "No description available"}
|
||||
</p>
|
||||
</div>
|
||||
<div className="card-footer d-flex justify-content-between align-items-center">
|
||||
<button className="btn btn-sm btn-link text-secondary fw-bold">
|
||||
<i className="fa fa-clock me-1"></i>
|
||||
Last Updated: {moment(app.updatedAt).format("DD/MM/YYYY")}
|
||||
</button>
|
||||
<span className="text-secondary">
|
||||
<i className="fa fa-calendar me-1"></i>
|
||||
{moment(app.createdAt).format("HH:mm:ss")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
{/* Add Modal */}
|
||||
{addModal && (
|
||||
<div className="modal show">
|
||||
<div className="modal-body">
|
||||
<div className="chart-box" onClick={handleAdd}>
|
||||
<img src="/assets/images/fromscratch.png" alt="From Scratch" />
|
||||
<h5>Start From Scratch</h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Delete Modal */}
|
||||
{modalDelete && rowSelected && (
|
||||
<div className="modal show">
|
||||
<div className="modal-body">
|
||||
<h1>Delete Confirmation</h1>
|
||||
<h2>{rowSelected.id}</h2>
|
||||
<div className="modal-footer">
|
||||
<button
|
||||
className="btn btn-outline-secondary"
|
||||
onClick={() => setModalDelete(false)}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
onClick={() => handleDelete(rowSelected.id)}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Toaster position="top-right" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardRunnerAll;
|
||||
@@ -0,0 +1,101 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import Chart from 'chart.js/auto';
|
||||
import axios from 'axios';
|
||||
|
||||
const BarRunnerComponent = ({ onButtonClicked }) => {
|
||||
const contentContainerRef = useRef(null);
|
||||
const { id: editId } = useParams();
|
||||
|
||||
const [barChartData, setBarChartData] = useState([]);
|
||||
const [barChartLabels, setBarChartLabels] = useState([]);
|
||||
const [barChartLegend, setBarChartLegend] = useState(false);
|
||||
const [tableName, setTableName] = useState('');
|
||||
const [xAxis, setXAxis] = useState('');
|
||||
const [yAxis, setYAxis] = useState('');
|
||||
const [showLabel, setShowLabel] = useState(false);
|
||||
const [jsonData, setJsonData] = useState(null);
|
||||
|
||||
const charData = {
|
||||
barChartData: [{ data: [45, 37, 60, 70, 46, 33], label: 'Best Fruits' }],
|
||||
barChartLabels: ['Apple', 'Banana', 'Kiwifruit', 'Blueberry', 'Orange', 'Grapes'],
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setBarChartData(charData.barChartData);
|
||||
setBarChartLabels(charData.barChartLabels);
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const response = await axios.get(`/dashboard/${editId}`);
|
||||
const workflowLine = response.data.dashbord1_Line[0].model;
|
||||
const dash = JSON.parse(workflowLine);
|
||||
const chartObject = dash.dashboard.filter(obj => obj.name === 'Bar Chart');
|
||||
|
||||
for (let i = 0; i < chartObject.length; i++) {
|
||||
const ids = getBarChartIds();
|
||||
|
||||
if (!ids.includes(chartObject[i].chartid)) {
|
||||
addBarChartId(chartObject[i].chartid);
|
||||
|
||||
setTableName(chartObject[i].table);
|
||||
setXAxis(chartObject[i].xAxis);
|
||||
setYAxis(chartObject[i].yAxis);
|
||||
setShowLabel(chartObject[i].showlabel);
|
||||
setBarChartLegend(chartObject[i].chartlegend);
|
||||
|
||||
const chartData = await getChartData(
|
||||
chartObject[i].table,
|
||||
'Bar Chart',
|
||||
chartObject[i].xAxis,
|
||||
chartObject[i].yAxis
|
||||
);
|
||||
|
||||
setJsonData(chartData);
|
||||
setBarChartData(chartData.barChartData);
|
||||
setBarChartLabels(chartData.barChartLabels);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
fetchData();
|
||||
}, [editId]);
|
||||
|
||||
const generatePDFFile = () => {
|
||||
if (onButtonClicked) onButtonClicked();
|
||||
|
||||
const content = contentContainerRef.current;
|
||||
const filename = 'barchart.pdf';
|
||||
generatePDF(content, filename);
|
||||
};
|
||||
|
||||
// Placeholder functions for service calls (replace these with actual API logic)
|
||||
const getBarChartIds = () => [];
|
||||
const addBarChartId = id => console.log(`Added chart id: ${id}`);
|
||||
const getChartData = async (table, type, xAxis, yAxis) => {
|
||||
// Replace with actual API call
|
||||
console.log(`Fetching chart data for table: ${table}, type: ${type}`);
|
||||
return charData;
|
||||
};
|
||||
const generatePDF = (content, filename) => {
|
||||
console.log(`Generating PDF for ${filename} with content:`, content);
|
||||
// Add logic for PDF generation
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button className="btn btn-primary" onClick={generatePDFFile}>
|
||||
Export
|
||||
</button>
|
||||
<div className="chart-box" id="contentContainer" ref={contentContainerRef}>
|
||||
<canvas id="barChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default BarRunnerComponent;
|
||||
@@ -0,0 +1,106 @@
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { Chart as ChartJS, BubbleController, Tooltip, Legend, Title } from "chart.js";
|
||||
import { Bubble } from "react-chartjs-2";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
// Register Chart.js components
|
||||
ChartJS.register(BubbleController, Tooltip, Legend, Title);
|
||||
|
||||
const BubbleRunnerComponent = ({ dashboardService, dashtestService }) => {
|
||||
const { id } = useParams(); // React Router hook to get route parameters
|
||||
const contentContainerRef = useRef(null);
|
||||
const [bubbleChartData, setBubbleChartData] = useState([]);
|
||||
const [chartLegend, setChartLegend] = useState(false);
|
||||
const [tableName, setTableName] = useState("");
|
||||
const [xAxis, setXAxis] = useState("");
|
||||
const [yAxis, setYAxis] = useState("");
|
||||
const [jsonData, setJsonData] = useState(null);
|
||||
|
||||
const bubbleChartOptions = {
|
||||
plugins: {
|
||||
title: {
|
||||
display: true,
|
||||
text: "Bubble Chart",
|
||||
},
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
min: 0,
|
||||
max: 30,
|
||||
},
|
||||
y: {
|
||||
min: 0,
|
||||
max: 30,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const fetchChartData = async (chartObject) => {
|
||||
const ids = dashtestService.getbubblechart();
|
||||
if (ids.includes(chartObject.chartid)) return;
|
||||
|
||||
dashtestService.setbubblechart(chartObject.chartid);
|
||||
setTableName(chartObject.table);
|
||||
setXAxis(chartObject.xAxis);
|
||||
setYAxis(chartObject.yAxis);
|
||||
setChartLegend(chartObject.chartlegend);
|
||||
|
||||
try {
|
||||
const data = await dashtestService.getChartData(chartObject.table, "Bubble Chart", chartObject.xAxis, chartObject.yAxis);
|
||||
setJsonData(data);
|
||||
setBubbleChartData(data.bubbleChartData);
|
||||
} catch (error) {
|
||||
console.error("Error fetching chart data:", error);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const fetchWorkflowData = async () => {
|
||||
try {
|
||||
const data = await dashboardService.getById(id);
|
||||
const workflowLine = data.dashbord1_Line[0].model;
|
||||
const parsedWorkflow = JSON.parse(workflowLine);
|
||||
const chartObjects = parsedWorkflow.dashboard.filter((obj) => obj.name === "Bubble Chart");
|
||||
|
||||
chartObjects.forEach(fetchChartData);
|
||||
} catch (error) {
|
||||
console.error("Error fetching workflow data:", error);
|
||||
}
|
||||
};
|
||||
|
||||
fetchWorkflowData();
|
||||
}, [id, dashboardService, dashtestService]);
|
||||
|
||||
const generatePDFFile = () => {
|
||||
if (!contentContainerRef.current) return;
|
||||
|
||||
const content = contentContainerRef.current;
|
||||
const filename = "bubblechart.pdf";
|
||||
|
||||
dashtestService.generatePDF(content, filename);
|
||||
};
|
||||
|
||||
const chartData = {
|
||||
datasets: bubbleChartData.map((data) => ({
|
||||
data: data.data,
|
||||
label: data.label,
|
||||
backgroundColor: data.backgroundColor,
|
||||
borderColor: data.borderColor,
|
||||
hoverBackgroundColor: data.hoverBackgroundColor,
|
||||
hoverBorderColor: data.hoverBorderColor,
|
||||
})),
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button className="btn btn-primary" onClick={generatePDFFile}>
|
||||
Export
|
||||
</button>
|
||||
<div className="chart-box" id="contentContainer" ref={contentContainerRef}>
|
||||
<Bubble data={chartData} options={bubbleChartOptions} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default BubbleRunnerComponent;
|
||||
@@ -0,0 +1,76 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
|
||||
const PieRunnerComponent = ({ editId, onButtonClicked, dashRunnerService, dashboardService }) => {
|
||||
const contentContainerRef = useRef(null);
|
||||
const [pieChartLabels, setPieChartLabels] = useState(['SciFi', 'Drama', 'Comedy']);
|
||||
const [pieChartData, setPieChartData] = useState([30, 50, 20]);
|
||||
const [chartLegend, setChartLegend] = useState(false);
|
||||
const [tableName, setTableName] = useState('');
|
||||
const [xAxis, setXAxis] = useState('');
|
||||
const [yAxis, setYAxis] = useState('');
|
||||
const [showLabel, setShowLabel] = useState(false);
|
||||
const [jsonData, setJsonData] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!editId) return;
|
||||
|
||||
dashboardService.getById(editId).then((data) => {
|
||||
const workflowLine = data.dashbord1_Line[0].model;
|
||||
const dash = JSON.parse(workflowLine);
|
||||
|
||||
const chartObject = dash.dashboard.filter((obj) => obj.name === 'Pie Chart');
|
||||
chartObject.forEach((chart) => {
|
||||
const ids = dashRunnerService.getPieChart();
|
||||
if (ids.includes(chart.chartid)) return;
|
||||
|
||||
dashRunnerService.setPieChart(chart.chartid);
|
||||
|
||||
if (chart.chartid === ids[0]) {
|
||||
setTableName(chart.table);
|
||||
setXAxis(chart.xAxis);
|
||||
setYAxis(chart.yAxis);
|
||||
setShowLabel(chart.showlabel);
|
||||
setChartLegend(chart.chartlegend);
|
||||
|
||||
dashRunnerService.getChartData(chart.table, 'Pie Chart', chart.xAxis, chart.yAxis).then((Ldata) => {
|
||||
setJsonData(Ldata);
|
||||
setPieChartLabels(Ldata.pieChartLabels);
|
||||
setPieChartData(Ldata.pieChartData);
|
||||
}).catch((error) => {
|
||||
console.error('Error fetching chart data:', error);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}, [editId, dashRunnerService, dashboardService]);
|
||||
|
||||
const generatePDFFile = () => {
|
||||
if (onButtonClicked) {
|
||||
onButtonClicked();
|
||||
}
|
||||
const content = contentContainerRef.current;
|
||||
const filename = 'piechart.pdf';
|
||||
|
||||
dashRunnerService.generatePDF(content, filename);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
<button className="btn btn-primary" onClick={generatePDFFile}>
|
||||
Export
|
||||
</button>
|
||||
</div>
|
||||
<div className="chart-box" id="contentContainer" ref={contentContainerRef}>
|
||||
<canvas
|
||||
style={{ display: 'block', width: '238px', height: '200px' }}
|
||||
>
|
||||
{/* Replace with a React chart library like 'react-chartjs-2' */}
|
||||
<p>Pie chart will render here using labels and data.</p>
|
||||
</canvas>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PieRunnerComponent;
|
||||
@@ -0,0 +1,98 @@
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { PolarArea } from 'react-chartjs-2';
|
||||
import html2canvas from 'html2canvas';
|
||||
import jsPDF from 'jspdf';
|
||||
import axios from 'axios';
|
||||
|
||||
const PolarRunnerComponent = ({ match }) => {
|
||||
const [editId, setEditId] = useState(null);
|
||||
const [polarAreaChartLabels, setPolarAreaChartLabels] = useState([
|
||||
'Download Sales',
|
||||
'In-Store Sales',
|
||||
'Mail Sales',
|
||||
'Telesales',
|
||||
'Corporate Sales',
|
||||
]);
|
||||
const [polarAreaChartData, setPolarAreaChartData] = useState([300, 500, 100, 40, 120]);
|
||||
const [chartLegend, setChartLegend] = useState(false);
|
||||
const [tableName, setTableName] = useState('');
|
||||
const [xAxis, setXAxis] = useState('');
|
||||
const [yAxis, setYAxis] = useState('');
|
||||
const [showLabel, setShowLabel] = useState(true);
|
||||
const [jsonData, setJsonData] = useState(null);
|
||||
|
||||
const contentContainerRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
const id = match.params.id;
|
||||
setEditId(id);
|
||||
|
||||
axios.get(`/api/dashboard/${id}`).then((response) => {
|
||||
const data = response.data;
|
||||
const workflowLine = data.dashbord1_Line[0].model;
|
||||
const dash = JSON.parse(workflowLine);
|
||||
|
||||
const chartObject = dash.dashboard.filter((obj) => obj.name === 'Polar Area Chart');
|
||||
chartObject.forEach((chart) => {
|
||||
if (chart.chartid) {
|
||||
setTableName(chart.table);
|
||||
setXAxis(chart.xAxis);
|
||||
setYAxis(chart.yAxis);
|
||||
setShowLabel(chart.showlabel);
|
||||
setChartLegend(chart.chartlegend);
|
||||
|
||||
axios
|
||||
.get(`/api/chart-data`, {
|
||||
params: {
|
||||
tableName: chart.table,
|
||||
chartType: 'PolarArea Chart',
|
||||
xAxis: chart.xAxis,
|
||||
yAxis: chart.yAxis,
|
||||
},
|
||||
})
|
||||
.then((chartData) => {
|
||||
setJsonData(chartData.data);
|
||||
setPolarAreaChartData(chartData.data.polarAreaChartData);
|
||||
setPolarAreaChartLabels(chartData.data.polarAreaChartLabels);
|
||||
})
|
||||
.catch((err) => console.error(err));
|
||||
}
|
||||
});
|
||||
});
|
||||
}, [match.params.id]);
|
||||
|
||||
const generatePDFFile = () => {
|
||||
const content = contentContainerRef.current;
|
||||
if (content) {
|
||||
html2canvas(content).then((canvas) => {
|
||||
const imgData = canvas.toDataURL('image/png');
|
||||
const pdf = new jsPDF();
|
||||
pdf.addImage(imgData, 'PNG', 0, 0);
|
||||
pdf.save('polarareachart.pdf');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const chartData = {
|
||||
labels: polarAreaChartLabels,
|
||||
datasets: [
|
||||
{
|
||||
data: polarAreaChartData,
|
||||
backgroundColor: ['#FF6384', '#36A2EB', '#FFCE56', '#4BC0C0', '#9966FF'],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button className="btn btn-primary" onClick={generatePDFFile}>
|
||||
Export
|
||||
</button>
|
||||
<div className="chart-box" id="contentContainer" ref={contentContainerRef}>
|
||||
<PolarArea data={chartData} options={{ plugins: { legend: { display: chartLegend } } }} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PolarRunnerComponent;
|
||||
@@ -0,0 +1,91 @@
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import DashrunnerService from "../services/DashrunnerService";
|
||||
import Dashboard3Service from "../services/Dashboard3Service";
|
||||
import { Chart } from "chart.js";
|
||||
|
||||
const RadarRunner = () => {
|
||||
const { id: editId } = useParams(); // Get 'id' from URL
|
||||
const contentContainerRef = useRef(null);
|
||||
const [radarChartLabels, setRadarChartLabels] = useState([
|
||||
"Eating",
|
||||
"Drinking",
|
||||
"Sleeping",
|
||||
"Designing",
|
||||
"Coding",
|
||||
"Cycling",
|
||||
"Running",
|
||||
]);
|
||||
const [radarChartData, setRadarChartData] = useState([
|
||||
{ data: [65, 59, 90, 81, 56, 55, 40], label: "Series A" },
|
||||
{ data: [28, 48, 40, 19, 96, 27, 100], label: "Series B" },
|
||||
]);
|
||||
const [showLabel, setShowLabel] = useState(true);
|
||||
const [lineChartNoLabels, setLineChartNoLabels] = useState([]);
|
||||
const [chartLegend, setChartLegend] = useState(false);
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const data = await Dashboard3Service.getById(editId);
|
||||
const workflowLine = data.dashbord1_Line[0].model;
|
||||
const parsedData = JSON.parse(workflowLine);
|
||||
|
||||
const chartObject = parsedData.dashboard.filter(
|
||||
(obj) => obj.name === "Radar Chart"
|
||||
);
|
||||
|
||||
for (let chart of chartObject) {
|
||||
const ids = DashrunnerService.getRadarChart();
|
||||
if (ids.includes(chart.chartid)) continue;
|
||||
|
||||
DashrunnerService.setRadarChart(chart.chartid);
|
||||
|
||||
if (chart.chartid === ids[0]) {
|
||||
const { table, xAxis, yAxis, showlabel, chartlegend } = chart;
|
||||
setChartLegend(chartlegend);
|
||||
setShowLabel(showlabel);
|
||||
|
||||
const chartData = await DashrunnerService.getChartData(
|
||||
table,
|
||||
"Radar Chart",
|
||||
xAxis,
|
||||
yAxis
|
||||
);
|
||||
setRadarChartData(chartData.radarChartData);
|
||||
setRadarChartLabels(chartData.radarChartLabels);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching data:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const generatePDFFile = () => {
|
||||
const content = contentContainerRef.current;
|
||||
const filename = "radarchart.pdf";
|
||||
DashrunnerService.generatePDF(content, filename);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, [editId]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
<button className="btn btn-primary" onClick={generatePDFFile}>
|
||||
Export
|
||||
</button>
|
||||
</div>
|
||||
<div className="chart-box" id="contentContainer" ref={contentContainerRef}>
|
||||
<canvas
|
||||
id="radarChart"
|
||||
style={{ display: "block", width: "238px", height: "200px" }}
|
||||
></canvas>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RadarRunner;
|
||||
@@ -0,0 +1,103 @@
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { Chart as ChartJS, registerables } from 'chart.js';
|
||||
import { Scatter } from 'react-chartjs-2';
|
||||
|
||||
ChartJS.register(...registerables);
|
||||
|
||||
const ScatterRunner = ({ dashboardService, dashrunnerService, routeParams }) => {
|
||||
const contentContainerRef = useRef(null);
|
||||
const [scatterChartData, setScatterChartData] = useState([]);
|
||||
const [scatterChartLabels, setScatterChartLabels] = useState([]);
|
||||
const [chartLegend, setChartLegend] = useState(false);
|
||||
const [editId, setEditId] = useState(routeParams.id || null);
|
||||
|
||||
const chartOptions = {
|
||||
responsive: true,
|
||||
aspectRatio: 2.5,
|
||||
};
|
||||
|
||||
const scatterChartDataTemplate = {
|
||||
datasets: [
|
||||
{
|
||||
data: [
|
||||
{ x: 1, y: 1 },
|
||||
{ x: 2, y: 3 },
|
||||
{ x: 3, y: -2 },
|
||||
{ x: 4, y: 4 },
|
||||
{ x: 5, y: -3, r: 20 },
|
||||
],
|
||||
label: 'Series A',
|
||||
pointRadius: 10,
|
||||
backgroundColor: 'red',
|
||||
},
|
||||
{
|
||||
data: [
|
||||
{ x: 2, y: 2 },
|
||||
{ x: 3, y: 4 },
|
||||
{ x: 4, y: -1 },
|
||||
{ x: 5, y: 5 },
|
||||
{ x: 6, y: -2, r: 20 },
|
||||
],
|
||||
label: 'Series B',
|
||||
pointRadius: 10,
|
||||
backgroundColor: 'blue',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (editId) {
|
||||
dashboardService.getById(editId).then((data) => {
|
||||
const workflowLine = data.dashbord1_Line[0]?.model;
|
||||
const dash = JSON.parse(workflowLine);
|
||||
|
||||
const chartObject = dash.dashboard.filter((obj) => obj.name === 'Scatter Chart');
|
||||
|
||||
chartObject.forEach((chart) => {
|
||||
const ids = dashrunnerService.getscatterchart();
|
||||
if (!ids.includes(chart.chartid)) {
|
||||
dashrunnerService.setscatterchart(chart.chartid);
|
||||
|
||||
dashrunnerService
|
||||
.getChartData(chart.table, 'Scatter Chart', chart.xAxis, chart.yAxis)
|
||||
.then((chartData) => {
|
||||
setScatterChartData(chartData.scatterChartData);
|
||||
setScatterChartLabels(chartData.scatterChartLabels);
|
||||
setChartLegend(chart.chartlegend);
|
||||
})
|
||||
.catch((error) => console.error(error));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}, [editId, dashboardService, dashrunnerService]);
|
||||
|
||||
const generatePDFFile = () => {
|
||||
if (contentContainerRef.current) {
|
||||
const content = contentContainerRef.current;
|
||||
const filename = 'scatterchart.pdf';
|
||||
dashrunnerService.generatePDF(content, filename);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
<button className="btn btn-primary" onClick={generatePDFFile}>
|
||||
Export
|
||||
</button>
|
||||
</div>
|
||||
<div className="chart-box" id="contentContainer" ref={contentContainerRef}>
|
||||
<Scatter
|
||||
data={{
|
||||
labels: scatterChartLabels,
|
||||
...scatterChartDataTemplate,
|
||||
}}
|
||||
options={chartOptions}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ScatterRunner;
|
||||
@@ -0,0 +1,95 @@
|
||||
import React, { useEffect, useState, useRef } from 'react';
|
||||
import { Doughnut } from 'react-chartjs-2';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { jsPDF } from 'jspdf';
|
||||
import DashrunnerService from '../services/DashrunnerService'; // Replace with actual service
|
||||
import Dashboard3Service from '../services/Dashboard3Service'; // Replace with actual service
|
||||
|
||||
const DoughnutRunner = () => {
|
||||
const { id } = useParams();
|
||||
const navigate = useNavigate();
|
||||
const contentContainerRef = useRef(null);
|
||||
|
||||
const [doughnutChartData, setDoughnutChartData] = useState([]);
|
||||
const [doughnutChartLabels, setDoughnutChartLabels] = useState([]);
|
||||
const [doughnutChartLegend, setDoughnutChartLegend] = useState(false);
|
||||
const [showlabel, setShowlabel] = useState(true);
|
||||
const [TableName, setTableName] = useState('');
|
||||
const [XAxis, setXAxis] = useState('');
|
||||
const [YAxis, setYAxis] = useState('');
|
||||
const [workflowLine, setWorkflowLine] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
Dashboard3Service.getById(id).then((data) => {
|
||||
const workflow = data.dashbord1_Line[0]?.model;
|
||||
const parsedData = JSON.parse(workflow);
|
||||
const chartObjects = parsedData.dashboard.filter(
|
||||
(obj) => obj.name === 'Doughnut Chart'
|
||||
);
|
||||
|
||||
for (let chartObject of chartObjects) {
|
||||
const ids = DashrunnerService.getDoughnutChartIds();
|
||||
if (ids.includes(chartObject.chartid)) continue;
|
||||
|
||||
DashrunnerService.setDoughnutChart(chartObject.chartid);
|
||||
|
||||
setTableName(chartObject.table);
|
||||
setXAxis(chartObject.xAxis);
|
||||
setYAxis(chartObject.yAxis);
|
||||
setShowlabel(chartObject.showlabel);
|
||||
setDoughnutChartLegend(chartObject.chartlegend);
|
||||
|
||||
DashrunnerService.getChartData(
|
||||
chartObject.table,
|
||||
'Doughnut Chart',
|
||||
chartObject.xAxis,
|
||||
chartObject.yAxis
|
||||
).then((chartData) => {
|
||||
setDoughnutChartData(chartData.chartData);
|
||||
setDoughnutChartLabels(chartData.chartLabels);
|
||||
});
|
||||
break;
|
||||
}
|
||||
});
|
||||
}, [id]);
|
||||
|
||||
const generatePDFFile = () => {
|
||||
const content = contentContainerRef.current;
|
||||
const pdf = new jsPDF();
|
||||
pdf.html(content, {
|
||||
callback: (doc) => {
|
||||
doc.save('doughnut.pdf');
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div style={{ textAlign: 'right' }}>
|
||||
<button className="btn btn-primary" onClick={generatePDFFile}>
|
||||
Export
|
||||
</button>
|
||||
</div>
|
||||
<div className="chart-box" ref={contentContainerRef}>
|
||||
<Doughnut
|
||||
data={{
|
||||
labels: showlabel ? doughnutChartLabels : [],
|
||||
datasets: [
|
||||
{
|
||||
data: doughnutChartData[0] || [],
|
||||
backgroundColor: ['#FF6384', '#36A2EB', '#FFCE56'],
|
||||
},
|
||||
],
|
||||
}}
|
||||
options={{
|
||||
legend: {
|
||||
display: doughnutChartLegend,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DoughnutRunner;
|
||||
@@ -0,0 +1,90 @@
|
||||
import React, { useEffect, useState, useRef } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
const GridRunnerComponent = ({ dashboardService, dashRunnerService }) => {
|
||||
const { id } = useParams(); // React Router for route parameters
|
||||
const contentContainerRef = useRef(null);
|
||||
const [rows, setRows] = useState([]);
|
||||
const [editId, setEditId] = useState(null);
|
||||
const [workflowLine, setWorkflowLine] = useState(null);
|
||||
const [tableName, setTableName] = useState(null);
|
||||
const [xAxis, setXAxis] = useState(null);
|
||||
const [yAxis, setYAxis] = useState(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setEditId(id);
|
||||
|
||||
dashboardService.getById(id).then((data) => {
|
||||
const lineData = data?.dashbord1_Line?.[0]?.model;
|
||||
setWorkflowLine(lineData);
|
||||
|
||||
const dash = JSON.parse(lineData || "{}");
|
||||
const chartObjects = dash.dashboard?.filter(
|
||||
(obj) => obj.name === "Grid View"
|
||||
) || [];
|
||||
|
||||
chartObjects.forEach((chartObject) => {
|
||||
const ids = dashRunnerService.getGridView();
|
||||
if (!ids.includes(chartObject.chartid)) {
|
||||
dashRunnerService.setGridView(chartObject.chartid);
|
||||
|
||||
if (chartObject.chartid === ids[0]) {
|
||||
setTableName(chartObject.table);
|
||||
setXAxis(chartObject.xAxis);
|
||||
setYAxis(chartObject.yAxis);
|
||||
|
||||
dashRunnerService
|
||||
.getChartData(chartObject.table, "Grid View", chartObject.xAxis, chartObject.yAxis)
|
||||
.then((data) => {
|
||||
setRows(data);
|
||||
})
|
||||
.catch((error) => console.error("Error fetching chart data:", error));
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}, [id, dashboardService, dashRunnerService]);
|
||||
|
||||
const getHeaders = () => {
|
||||
if (!rows || rows.length === 0) return [];
|
||||
return Object.keys(rows[0]);
|
||||
};
|
||||
|
||||
const generatePDFFile = () => {
|
||||
if (!contentContainerRef.current) return;
|
||||
const content = contentContainerRef.current;
|
||||
const filename = "gridview.pdf";
|
||||
dashRunnerService.generatePDF(content, filename);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button className="btn btn-primary" onClick={generatePDFFile}>
|
||||
Export
|
||||
</button>
|
||||
<div style={{ maxHeight: "400px", overflow: "auto", padding: "10px" }}>
|
||||
<table className="table">
|
||||
<thead>
|
||||
<tr>
|
||||
{getHeaders().map((header, index) => (
|
||||
<th key={index}>{header}</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{rows.slice().reverse().map((row, rowIndex) => (
|
||||
<tr key={rowIndex}>
|
||||
{getHeaders().map((key, colIndex) => (
|
||||
<td key={colIndex}>{row[key]}</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default GridRunnerComponent;
|
||||
@@ -0,0 +1,80 @@
|
||||
import React, { useState, useEffect, useRef } from "react";
|
||||
import { Chart } from "chart.js";
|
||||
import { jsPDF } from "jspdf";
|
||||
import domtoimage from "dom-to-image";
|
||||
|
||||
const LineRunnerComponent = ({ dashrunnerService, dashboardService }) => {
|
||||
const contentContainerRef = useRef(null);
|
||||
const [lineChartData, setLineChartData] = useState([]);
|
||||
const [lineChartLabels, setLineChartLabels] = useState([]);
|
||||
const [lineChartNoLabels] = useState([]);
|
||||
const [lineChartLegend, setLineChartLegend] = useState(false);
|
||||
const [workflowLine, setWorkflowLine] = useState(null);
|
||||
const [tableName, setTableName] = useState(null);
|
||||
const [xAxis, setXAxis] = useState(null);
|
||||
const [yAxis, setYAxis] = useState(null);
|
||||
const [showLabel, setShowLabel] = useState(null);
|
||||
const [editId, setEditId] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
// Mock equivalent of Angular's ActivatedRoute for id retrieval
|
||||
const idFromRoute = "mockId"; // Replace with actual route logic
|
||||
setEditId(idFromRoute);
|
||||
|
||||
dashboardService.getById(idFromRoute).then((data) => {
|
||||
const workflow = data.dashbord1_Line[0].model;
|
||||
setWorkflowLine(workflow);
|
||||
|
||||
const parsedData = JSON.parse(workflow);
|
||||
const chartObjects = parsedData.dashboard.filter(
|
||||
(obj) => obj.name === "Line Chart"
|
||||
);
|
||||
|
||||
chartObjects.forEach((chart) => {
|
||||
const ids = dashrunnerService.getlinechart();
|
||||
if (!ids.includes(chart.chartid)) {
|
||||
dashrunnerService.setlinechart(chart.chartid);
|
||||
|
||||
if (chart.chartid === ids[0]) {
|
||||
setTableName(chart.table);
|
||||
setXAxis(chart.xAxis);
|
||||
setYAxis(chart.yAxis);
|
||||
setShowLabel(chart.showlabel);
|
||||
setLineChartLegend(chart.chartlegend);
|
||||
|
||||
dashrunnerService
|
||||
.getChartData(chart.table, "Line Chart", chart.xAxis, chart.yAxis)
|
||||
.then((Ldata) => {
|
||||
setLineChartData(Ldata.chartData);
|
||||
setLineChartLabels(Ldata.chartLabels);
|
||||
})
|
||||
.catch((error) => console.error(error));
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}, [dashrunnerService, dashboardService]);
|
||||
|
||||
const generatePDFFile = () => {
|
||||
const content = contentContainerRef.current;
|
||||
const filename = "linechart.pdf";
|
||||
|
||||
dashrunnerService.generatePDF(content, filename);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button className="btn btn-primary" onClick={generatePDFFile}>
|
||||
Export
|
||||
</button>
|
||||
<div className="chart-box" id="contentContainer" ref={contentContainerRef}>
|
||||
<canvas
|
||||
id="lineChart"
|
||||
style={{ display: "block" }}
|
||||
></canvas>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LineRunnerComponent;
|
||||
@@ -0,0 +1,197 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useNavigate, useParams,useLocation } from 'react-router-dom';
|
||||
import {toast} from 'react-toastify'; // You can use a different notification system if needed
|
||||
import DashboardBuilderService from "../../../../APIServices/DashboardBuilderService"; // Update with your service path
|
||||
import { Badge } from "reactstrap";
|
||||
|
||||
const EditFormNewDash = () => {
|
||||
const { id } = useParams(); // Access the id from the URL params
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const [header, setHeader] = useState({
|
||||
dashboard_name: '',
|
||||
secuirity_profile: '',
|
||||
description: '',
|
||||
add_to_home: false,
|
||||
});
|
||||
const [updated, setUpdated] = useState(false);
|
||||
const [techStacks, setTechStacks] = useState([]);
|
||||
const objectTypes = ["form", "bi", "report", "api"];
|
||||
const subObjectTypes = [
|
||||
"only header",
|
||||
"only line",
|
||||
"header line",
|
||||
"header multiline",
|
||||
"workflow",
|
||||
"setup",
|
||||
"std report",
|
||||
"bi report",
|
||||
"rest api",
|
||||
];
|
||||
|
||||
// Fetch data by ID when component mounts
|
||||
// useEffect(() => {
|
||||
// if (id) {
|
||||
// getById(id);
|
||||
// }
|
||||
// }, [id]);
|
||||
|
||||
useEffect(() => {
|
||||
if (location.state?.dashboard) {
|
||||
setHeader(location.state.dashboard);
|
||||
} else {
|
||||
// Fetch data if not passed in state
|
||||
getById(id);
|
||||
}
|
||||
}, [id, location.state]);
|
||||
|
||||
// Get dashboard data by ID
|
||||
// const getById = (id) => {
|
||||
// DashboardService.getById(id)
|
||||
// .then((data) => {
|
||||
// console.log(data);
|
||||
// setHeader(data);
|
||||
// })
|
||||
// .catch((err) => {
|
||||
// console.log(err);
|
||||
// });
|
||||
// };
|
||||
|
||||
const getById = (id,data) => {
|
||||
|
||||
DashboardBuilderService.getById(id)
|
||||
.then((data) => {
|
||||
console.log("get dashbord by id: ",data);
|
||||
setHeader(data);
|
||||
})
|
||||
.catch((err) => {
|
||||
// Static error handling
|
||||
console.error("Error fetching dashboard by ID:", err);
|
||||
toast.error('Failed to fetch dashboard data. Please try again later.');
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
const handleInputChange = (e) => {
|
||||
const { name, value, type, checked } = e.target;
|
||||
setHeader((prev) => ({
|
||||
...prev,
|
||||
...header,
|
||||
[name]: type === 'checkbox' ? checked : value,
|
||||
}));
|
||||
};
|
||||
|
||||
const update = async () => {
|
||||
|
||||
console.log("Submitted Data:", header); // Log the data to verify it's correct
|
||||
const staticTestData = { ...header}; // Mock updated data with additional fields if needed
|
||||
console.log("Static Updated Data:", staticTestData);
|
||||
toast.success('Dashboard updated successfully');
|
||||
// navigate('/admin/dashboard-new-all',{ state: { updatedDashboard: staticTestData } });
|
||||
|
||||
try {
|
||||
const updatedDashboard = await DashboardBuilderService.updateDash(header); // Await the API response
|
||||
|
||||
if (updatedDashboard) {
|
||||
setUpdated(true);
|
||||
toast.success('Dashboard updated successfully');
|
||||
navigate('/admin/dashboard-new-all'); // Pass updated data to the next page
|
||||
} else {
|
||||
throw new Error("No response from server");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating dashboard:", error);
|
||||
toast.error('Failed to update the dashboard. Please try again later.');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Handle form submission
|
||||
const onSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
setUpdated(true);
|
||||
update();
|
||||
};
|
||||
|
||||
// Handle the back button
|
||||
const onBack = () => {
|
||||
navigate('/admin/dashboard-new-all');
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
<h4 style={{ fontWeight: 300, display: 'inline' }}><b> Dashboard</b></h4>
|
||||
<Badge
|
||||
color="info" // This sets the blue color
|
||||
style={{ display: "inline", marginLeft: 10 }}
|
||||
>
|
||||
Edit Mode
|
||||
</Badge>
|
||||
<hr /><br />
|
||||
|
||||
<form onSubmit={onSubmit}>
|
||||
<div className="row">
|
||||
<div className="col-md-6 col-sm-12">
|
||||
<label htmlFor="dashboard_name">Dashboard Name</label>
|
||||
<input
|
||||
id="dashboard_name"
|
||||
type="text"
|
||||
name="dashboard_name"
|
||||
value={header.dashboard_name || ''}
|
||||
// onChange={(e) => setHeader({ ...header, dashboard_name: e.target.value })}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Enter Dashboard Name"
|
||||
className="form-control"
|
||||
/>
|
||||
</div>
|
||||
<div className="col-md-6 col-sm-12">
|
||||
<label htmlFor="secuirity_profile">Security Profile</label>
|
||||
<input
|
||||
id="secuirity_profile"
|
||||
type="text"
|
||||
name="secuirity_profile"
|
||||
value={header.secuirity_profile || ''}
|
||||
// onChange={(e) => setHeader({ ...header, secuirity_profile: e.target.value })}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Enter Security Profile"
|
||||
className="form-control"
|
||||
/>
|
||||
</div>
|
||||
<div className="col-md-6 col-sm-12">
|
||||
<label htmlFor="description">Description</label>
|
||||
<textarea
|
||||
cols="10"
|
||||
rows="2"
|
||||
name="description"
|
||||
value={header.description || ''}
|
||||
// onChange={(e) => setHeader({ ...header, description: e.target.value })}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Enter Description"
|
||||
className="form-control"
|
||||
/>
|
||||
</div>
|
||||
<div className="col-md-6 col-sm-12">
|
||||
<label htmlFor="workflow_name" style={{ marginTop: "1rem" }}>Add to Dashboard</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
name="add_to_home"
|
||||
checked={header.add_to_home || ''}
|
||||
// onChange={(e) => setHeader({ ...header, add_to_home: e.target.checked })}
|
||||
onChange={handleInputChange}
|
||||
className="form-check-input"
|
||||
style={{ marginTop: "3rem", marginLeft: "-8rem" }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
<div className="text-center">
|
||||
<button type="button" className="btn btn-outline-secondary" onClick={onBack}>BACK</button>
|
||||
<button type="submit" className="btn btn-primary">Update</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditFormNewDash;
|
||||
605
src/components/Dashboard/dashboardnew/EditNewDash/EditNewDash.js
Normal file
605
src/components/Dashboard/dashboardnew/EditNewDash/EditNewDash.js
Normal file
@@ -0,0 +1,605 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import 'react-grid-layout/css/styles.css';
|
||||
import 'react-resizable/css/styles.css';
|
||||
import { Responsive, WidthProvider } from 'react-grid-layout';
|
||||
import { FormControl, Input, Select, Checkbox, Button, Dialog, DialogActions, DialogContent, DialogTitle, Typography, Card, CardContent, MenuItem, Box } from '@mui/material';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faTrash, faGripLines, faPen } from '@fortawesome/free-solid-svg-icons';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import DashboardBuilderService from "../../../../APIServices/DashboardBuilderService";
|
||||
|
||||
// Import chart components
|
||||
import LineChartComponent from '../gadgets/line-chart/LineChart';
|
||||
import PieChartComponent from '../gadgets/pie-chart/Piechart';
|
||||
import PolarChartComponent from '../gadgets/polar-chart/PolarChart';
|
||||
import RadarChartComponent from '../gadgets/radar-chart/RadarChart';
|
||||
import BubbleChart from '../gadgets/bubble-chart/BubbleChart';
|
||||
import BarChart from '../gadgets/bar-chart/BarChart';
|
||||
import DoughnutChart from '../gadgets/doughnut-charts/DoughnutChart';
|
||||
import DynamicChart from '../gadgets/dynamic-chart/DynamicChart';
|
||||
import FinancialChart from '../gadgets/financial-chart/FinancialChart';
|
||||
import GridViewComponent from '../gadgets/grid-view/GridView';
|
||||
import ScatterChartComponent from '../gadgets/scatter-chart/ScatterChart';
|
||||
import ToDoChartComponent from '../gadgets/to-do-chart/TodoChart';
|
||||
|
||||
// Use WidthProvider to handle responsive behavior
|
||||
const ResponsiveGridLayout = WidthProvider(Responsive);
|
||||
|
||||
const EditNewDash = () => {
|
||||
const { id } = useParams(); // fetching id from the url
|
||||
const [sidebarOpen, setSidebarOpen] = useState(false);
|
||||
const [editModalOpen, setEditModalOpen] = useState(false);
|
||||
const [currentEditWidget, setCurrentEditWidget] = useState({});
|
||||
const [dashboardName, setDashboardName] = useState();
|
||||
const [dashboardLine, setDashboardLine] = useState();
|
||||
const [columnData] = useState(['Column1', 'Column2', 'Column3']);
|
||||
const [dashboardWidgets, setDashboardWidgets] = useState([]);
|
||||
const [dashboardlineId, setDashboardlineId]= useState();
|
||||
|
||||
// Define available widgets
|
||||
const [availableWidgets] = useState([
|
||||
{ identifier: 'radar_chart', name: 'Radar Chart' },
|
||||
{ identifier: 'polar_chart', name: 'Polar Chart' },
|
||||
{ identifier: 'pie_chart', name: 'Pie Chart' },
|
||||
{ identifier: 'bar_chart', name: 'Bar Chart' },
|
||||
{ identifier: 'bubble_chart', name: 'Bubble Chart' },
|
||||
{ identifier: 'line_chart', name: 'Line Chart' },
|
||||
{ identifier: 'doughnut_chart', name: 'Doughnut Chart' },
|
||||
{ identifier: 'dynamic_chart', name: 'Dynamic Chart' },
|
||||
{ identifier: 'financial_chart', name: 'Financial Chart' },
|
||||
{ identifier: 'grid_view', name: 'Grid View' },
|
||||
{ identifier: 'scatter_chart', name: 'Scatter Chart' },
|
||||
{ identifier: 'todo_chart', name: 'Todo Chart' },
|
||||
]);
|
||||
|
||||
// Define component mapping object for easier lookup
|
||||
const componentMap = {
|
||||
'radar_chart': RadarChartComponent,
|
||||
'polar_chart': PolarChartComponent,
|
||||
'pie_chart': PieChartComponent,
|
||||
'bar_chart': BarChart,
|
||||
'bubble_chart': BubbleChart,
|
||||
'line_chart': LineChartComponent,
|
||||
'doughnut_chart': DoughnutChart,
|
||||
'dynamic_chart': DynamicChart,
|
||||
'financial_chart': FinancialChart,
|
||||
'grid_view': GridViewComponent,
|
||||
'scatter_chart': ScatterChartComponent,
|
||||
'todo_chart': ToDoChartComponent
|
||||
};
|
||||
|
||||
// Component name to identifier mapping for reverse lookup
|
||||
const componentNameToIdentifier = {
|
||||
'Radar Chart': 'radar_chart',
|
||||
'Polar Chart': 'polar_chart',
|
||||
'Pie Chart': 'pie_chart',
|
||||
'Bar Chart': 'bar_chart',
|
||||
'Bubble Chart': 'bubble_chart',
|
||||
'Line Chart': 'line_chart',
|
||||
'Doughnut Chart': 'doughnut_chart',
|
||||
'Dynamic Chart': 'dynamic_chart',
|
||||
'Financial Chart': 'financial_chart',
|
||||
'Grid View': 'grid_view',
|
||||
'Scatter Chart': 'scatter_chart',
|
||||
'Todo Chart': 'todo_chart'
|
||||
};
|
||||
|
||||
// fetching dashboard data by id (id is fetched from the url)
|
||||
useEffect(() => {
|
||||
fetchDashboardData();
|
||||
}, []);
|
||||
|
||||
const fetchDashboardData = async () => {
|
||||
try {
|
||||
const response = await DashboardBuilderService.getById(id);
|
||||
console.log("Raw API response:", response);
|
||||
console.log("dashboardid: ",response.dashbord1_Line[0].id)
|
||||
setDashboardlineId(response.dashbord1_Line[0].id)
|
||||
setDashboardName(response.dashboard_name);
|
||||
setDashboardLine(response.dashbord1_Line);
|
||||
|
||||
if (response.dashbord1_Line && response.dashbord1_Line[0]?.model) {
|
||||
try {
|
||||
const modelData = response.dashbord1_Line[0].model;
|
||||
console.log("Model Data:", modelData);
|
||||
|
||||
const parsedModel = JSON.parse(modelData);
|
||||
console.log("Parsed Model:", parsedModel);
|
||||
|
||||
// Transform the data format to match what ResponsiveGridLayout expects
|
||||
const transformedWidgets = Array.isArray(parsedModel.dashboard)
|
||||
? parsedModel.dashboard.map((widget, index) => {
|
||||
// Create a widget ID if not present
|
||||
const widgetId = widget.i || `widget-${index}`;
|
||||
|
||||
// Get the component identifier from name if component is provided as a string
|
||||
const typeIdentifier = widget.type ||
|
||||
(widget.component && typeof widget.component === 'string'
|
||||
? componentNameToIdentifier[widget.component] || widget.component.toLowerCase().replace(' ', '_')
|
||||
: 'unknown');
|
||||
|
||||
// Get component reference from the component map
|
||||
const componentRef = componentMap[typeIdentifier] || null;
|
||||
|
||||
// Map the widget properties to the expected format
|
||||
return {
|
||||
i: widgetId,
|
||||
x: widget.x || 0,
|
||||
y: widget.y || 0,
|
||||
w: widget.cols || 6, // Convert cols to w
|
||||
h: widget.rows || 8, // Convert rows to h
|
||||
type: typeIdentifier,
|
||||
name: widget.name || availableWidgets.find(w => w.identifier === typeIdentifier)?.name || 'Unknown Widget',
|
||||
component: componentRef,
|
||||
chartTitle: widget.chartTitle,
|
||||
showLegend: widget.showLegend,
|
||||
showLabel: widget.showLabel,
|
||||
xAxis: widget.xAxis,
|
||||
yAxis: widget.yAxis
|
||||
};
|
||||
})
|
||||
: [];
|
||||
|
||||
console.log("Transformed widgets:", transformedWidgets);
|
||||
setDashboardWidgets(transformedWidgets);
|
||||
} catch (parseError) {
|
||||
console.error("Error parsing dashboard model:", parseError);
|
||||
setDashboardWidgets([]);
|
||||
}
|
||||
} else {
|
||||
console.log("No valid model data, setting empty widgets.");
|
||||
setDashboardWidgets([]);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching dashboard data:", error);
|
||||
setDashboardWidgets([]);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
console.log("Updated dashboardWidgets:", dashboardWidgets);
|
||||
}, [dashboardWidgets]);
|
||||
|
||||
const [dashboardForm, setDashboardForm] = useState({
|
||||
donut: '',
|
||||
chartlegend: '',
|
||||
showlabel: '',
|
||||
charturl: '',
|
||||
chartparameter: '',
|
||||
datastore: '',
|
||||
table: '',
|
||||
datasource: '',
|
||||
charttitle: '',
|
||||
id: '',
|
||||
fieldName: '',
|
||||
chartcolor: '',
|
||||
slices: '',
|
||||
yAxis: '',
|
||||
xAxis: '',
|
||||
});
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
// Handle widget drag from sidebar
|
||||
const onDragStart = (e, widget) => {
|
||||
e.dataTransfer.setData('widgetType', widget.identifier);
|
||||
};
|
||||
|
||||
// Handle widget drop onto dashboard
|
||||
const onDragOver = (e) => {
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
const onDrop = (e) => {
|
||||
e.preventDefault();
|
||||
const widgetType = e.dataTransfer.getData('widgetType');
|
||||
|
||||
if (!widgetType) return;
|
||||
|
||||
// Calculate drop position relative to the grid
|
||||
const gridRect = e.currentTarget.getBoundingClientRect();
|
||||
const x = Math.floor((e.clientX - gridRect.left) / 100) * 2;
|
||||
const y = Math.floor((e.clientY - gridRect.top) / 100) * 2;
|
||||
|
||||
// Create new widget based on type
|
||||
let maxChartId = dashboardWidgets?.reduce((maxId, item) => Math.max(maxId, item.chartid || 0), 0);
|
||||
console.log("maxChartId",maxChartId)
|
||||
const newWidgetId = `widget-${Date.now()}`;
|
||||
const componentRef = componentMap[widgetType] || null;
|
||||
const widgetName = availableWidgets.find(w => w.identifier === widgetType)?.name || 'Unknown Widget';
|
||||
|
||||
const newWidget = {
|
||||
i: newWidgetId,
|
||||
x: x,
|
||||
y: y,
|
||||
w: 6,
|
||||
h: 8,
|
||||
type: widgetType,
|
||||
name: widgetName,
|
||||
component: componentRef, // Add the component reference here
|
||||
chartTitle: widgetName, // Add default chart title
|
||||
showLegend: true, // Add default values for chart config
|
||||
showLabel: true,
|
||||
xAxis: 'Month',
|
||||
yAxis: 'Value'
|
||||
};
|
||||
|
||||
setDashboardWidgets(prev => [...prev, newWidget]);
|
||||
};
|
||||
|
||||
// Handle layout changes
|
||||
const onLayoutChange = (layout) => {
|
||||
// Update widget positions when layout changes
|
||||
const updatedWidgets = dashboardWidgets.map(widget => {
|
||||
const updatedPosition = layout.find(item => item.i === widget.i);
|
||||
if (updatedPosition) {
|
||||
return { ...widget, x: updatedPosition.x, y: updatedPosition.y, w: updatedPosition.w, h: updatedPosition.h };
|
||||
}
|
||||
return widget;
|
||||
});
|
||||
|
||||
setDashboardWidgets(updatedWidgets);
|
||||
};
|
||||
|
||||
// Remove widget
|
||||
const removeWidget = (widgetId) => {
|
||||
setDashboardWidgets(prev => prev.filter(widget => widget.i !== widgetId));
|
||||
};
|
||||
|
||||
// Edit widget
|
||||
const editWidget = (widget) => {
|
||||
setCurrentEditWidget(widget);
|
||||
setEditModalOpen(true);
|
||||
};
|
||||
|
||||
// Save edited widget
|
||||
const saveWidgetChanges = () => {
|
||||
setDashboardWidgets(prev =>
|
||||
prev.map(widget =>
|
||||
widget.i === currentEditWidget.i ? { ...widget, ...currentEditWidget } : widget
|
||||
)
|
||||
);
|
||||
setEditModalOpen(false);
|
||||
};
|
||||
|
||||
// Navigate back
|
||||
const goBack = () => {
|
||||
navigate('/admin/dashboard-new-all');
|
||||
};
|
||||
|
||||
const dashbord1_Line = {
|
||||
model:''
|
||||
}
|
||||
// Save dashboard
|
||||
const saveDashboard = async () => {
|
||||
try {
|
||||
// Ensure dashboardWidgets is not empty before saving
|
||||
if (!dashboardWidgets || dashboardWidgets.length === 0) {
|
||||
console.warn("No widgets to save.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare the dashboard data
|
||||
const dashboardData = {
|
||||
dashboard: dashboardWidgets.map(widget => ({
|
||||
x: widget.x ?? 0, // Default values to prevent errors
|
||||
y: widget.y ?? 0,
|
||||
cols: widget.w ?? 1,
|
||||
rows: widget.h ?? 1,
|
||||
component: widget.name || "Unknown",
|
||||
name: widget.name || "Unknown",
|
||||
// type: widget.type || "Generic",
|
||||
})),
|
||||
};
|
||||
|
||||
console.log("Saving dashboard:", dashboardData);
|
||||
console.log("Saving dashboard:", JSON.stringify(dashboardData))
|
||||
dashbord1_Line.model = JSON.stringify(dashboardData)
|
||||
console.log("dashbord1_Line: ", dashbord1_Line)
|
||||
// Call the API to update the dashboard data
|
||||
const response = await DashboardBuilderService.updateLineData(dashboardlineId, dashbord1_Line);
|
||||
|
||||
if (response) {
|
||||
console.log("Dashboard saved successfully:", response.data);
|
||||
// Optionally navigate after saving
|
||||
navigate('/admin/dashboard-new-all');
|
||||
} else {
|
||||
console.error("Failed to save dashboard. Server response:", response);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error saving dashboard:", error.message || error);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: '100vh',
|
||||
padding: 3
|
||||
}}>
|
||||
{/* Toolbar */}
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
width: '100%',
|
||||
marginBottom: 3
|
||||
}}>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={() => setSidebarOpen(!sidebarOpen)}
|
||||
>
|
||||
{sidebarOpen ? 'Hide Components' : 'Add Components'}
|
||||
</Button>
|
||||
|
||||
<Typography variant="h5">{dashboardName}</Typography>
|
||||
|
||||
<Box>
|
||||
<Button
|
||||
variant="outlined"
|
||||
sx={{ marginRight: 1 }}
|
||||
onClick={goBack}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={saveDashboard}
|
||||
>
|
||||
Save Dashboard
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* Main content */}
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
flex: 1,
|
||||
width: '100%',
|
||||
overflow: 'hidden'
|
||||
}}>
|
||||
{/* Sidebar */}
|
||||
{sidebarOpen && (
|
||||
<Box sx={{
|
||||
width: 250,
|
||||
backgroundColor: '#f5f5f5',
|
||||
padding: 2,
|
||||
overflowY: 'auto',
|
||||
borderRight: '1px solid #e0e0e0'
|
||||
}}>
|
||||
<Typography variant="h6" sx={{ marginBottom: 2 }}>
|
||||
Available Components
|
||||
</Typography>
|
||||
<Box component="ul" sx={{
|
||||
listStyleType: 'none',
|
||||
padding: 0,
|
||||
margin: 0
|
||||
}}>
|
||||
{availableWidgets.map(widget => (
|
||||
<Box
|
||||
component="li"
|
||||
key={widget.identifier}
|
||||
sx={{
|
||||
padding: 1.5,
|
||||
marginBottom: 1,
|
||||
backgroundColor: 'white',
|
||||
borderRadius: 1,
|
||||
cursor: 'move',
|
||||
border: '1px solid #e0e0e0',
|
||||
'&:hover': {
|
||||
backgroundColor: '#f0f7ff'
|
||||
}
|
||||
}}
|
||||
draggable
|
||||
onDragStart={(e) => onDragStart(e, widget)}
|
||||
>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
<FontAwesomeIcon icon={faGripLines} style={{ marginRight: 10 }} />
|
||||
{widget.name}
|
||||
</Box>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Dashboard grid */}
|
||||
<Box
|
||||
sx={{
|
||||
flex: 1,
|
||||
padding: 2,
|
||||
overflow: 'auto',
|
||||
backgroundColor: '#fafafa'
|
||||
}}
|
||||
onDragOver={onDragOver}
|
||||
onDrop={onDrop}
|
||||
>
|
||||
<ResponsiveGridLayout
|
||||
className="layout"
|
||||
layouts={{
|
||||
lg: dashboardWidgets.map(widget => ({
|
||||
i: widget.i,
|
||||
x: widget.x,
|
||||
y: widget.y,
|
||||
w: widget.w,
|
||||
h: widget.h,
|
||||
minW: 3,
|
||||
minH: 3
|
||||
}))
|
||||
}}
|
||||
breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
|
||||
cols={{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }}
|
||||
rowHeight={30}
|
||||
onLayoutChange={onLayoutChange}
|
||||
isDraggable
|
||||
isResizable
|
||||
margin={[20, 20]}
|
||||
>
|
||||
{dashboardWidgets.map(widget => (
|
||||
<Box key={widget.i} sx={{ overflow: 'hidden' }}>
|
||||
<Card sx={{
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
boxShadow: '0 4px 8px rgba(0, 0, 0, 0.1)'
|
||||
}}>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
padding: 1,
|
||||
borderBottom: '1px solid #eee'
|
||||
}}>
|
||||
<Typography variant="subtitle1" sx={{ fontWeight: 'bold' }}>
|
||||
{widget.name}
|
||||
</Typography>
|
||||
<Box>
|
||||
<Button
|
||||
size="small"
|
||||
onClick={() => editWidget(widget)}
|
||||
sx={{ minWidth: 'auto', padding: '4px' }}
|
||||
>
|
||||
<FontAwesomeIcon icon={faPen} />
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
color="error"
|
||||
onClick={() => removeWidget(widget.i)}
|
||||
sx={{ minWidth: 'auto', padding: '4px', ml: 1 }}
|
||||
>
|
||||
<FontAwesomeIcon icon={faTrash} />
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
<CardContent sx={{
|
||||
flex: 1,
|
||||
padding: 2,
|
||||
overflow: 'hidden',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: 'calc(100% - 50px)' // Subtracting header height
|
||||
}}>
|
||||
{widget.component && (
|
||||
<Box sx={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
overflow: 'hidden'
|
||||
}}>
|
||||
<widget.component
|
||||
chartTitle={widget.chartTitle || widget.name}
|
||||
showLegend={widget.showLegend !== undefined ? widget.showLegend : true}
|
||||
showLabel={widget.showLabel !== undefined ? widget.showLabel : true}
|
||||
xAxis={widget.xAxis || 'Month'}
|
||||
yAxis={widget.yAxis || 'Value'}
|
||||
width="100%"
|
||||
height="100%"
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Box>
|
||||
))}
|
||||
</ResponsiveGridLayout>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* Edit widget dialog */}
|
||||
<Dialog open={editModalOpen} onClose={() => setEditModalOpen(false)} maxWidth="sm" fullWidth>
|
||||
<DialogTitle>Edit {currentEditWidget.name}</DialogTitle>
|
||||
<DialogContent sx={{ paddingTop: 2 }}>
|
||||
<FormControl fullWidth sx={{ marginBottom: 2 }}>
|
||||
<Typography variant="subtitle2" sx={{ marginBottom: 1 }}>Chart Title</Typography>
|
||||
<Input
|
||||
value={currentEditWidget.chartTitle || ''}
|
||||
onChange={(e) => setCurrentEditWidget({
|
||||
...currentEditWidget,
|
||||
chartTitle: e.target.value
|
||||
})}
|
||||
placeholder="Enter chart title"
|
||||
fullWidth
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
{currentEditWidget.name !== 'Grid View' && currentEditWidget.name !== 'Todo Chart' && (
|
||||
<>
|
||||
<Box sx={{ display: 'flex', gap: 2, marginBottom: 2 }}>
|
||||
<FormControl sx={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
|
||||
<Checkbox
|
||||
checked={currentEditWidget.showLegend || false}
|
||||
onChange={(e) => setCurrentEditWidget({
|
||||
...currentEditWidget,
|
||||
showLegend: e.target.checked
|
||||
})}
|
||||
/>
|
||||
<Typography>Show Legend</Typography>
|
||||
</FormControl>
|
||||
|
||||
<FormControl sx={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
|
||||
<Checkbox
|
||||
checked={currentEditWidget.showLabel || false}
|
||||
onChange={(e) => setCurrentEditWidget({
|
||||
...currentEditWidget,
|
||||
showLabel: e.target.checked
|
||||
})}
|
||||
/>
|
||||
<Typography>Show Labels</Typography>
|
||||
</FormControl>
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
|
||||
<FormControl fullWidth sx={{ marginBottom: 2 }}>
|
||||
<Typography variant="subtitle2" sx={{ marginBottom: 1 }}>X-Axis</Typography>
|
||||
<Select
|
||||
value={currentEditWidget.xAxis || ''}
|
||||
onChange={(e) => setCurrentEditWidget({
|
||||
...currentEditWidget,
|
||||
xAxis: e.target.value
|
||||
})}
|
||||
displayEmpty
|
||||
>
|
||||
<MenuItem value="" disabled>Select X-Axis</MenuItem>
|
||||
{columnData.map((col, index) => (
|
||||
<MenuItem key={index} value={col}>{col}</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<FormControl fullWidth sx={{ marginBottom: 2 }}>
|
||||
<Typography variant="subtitle2" sx={{ marginBottom: 1 }}>Y-Axis</Typography>
|
||||
<Select
|
||||
value={currentEditWidget.yAxis || ''}
|
||||
onChange={(e) => setCurrentEditWidget({
|
||||
...currentEditWidget,
|
||||
yAxis: e.target.value
|
||||
})}
|
||||
displayEmpty
|
||||
>
|
||||
<MenuItem value="" disabled>Select Y-Axis</MenuItem>
|
||||
{columnData.map((col, index) => (
|
||||
<MenuItem key={index} value={col}>{col}</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</DialogContent>
|
||||
<DialogActions sx={{ padding: 2 }}>
|
||||
<Button onClick={() => setEditModalOpen(false)} color="inherit">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={saveWidgetChanges} variant="contained" color="primary">
|
||||
Save Changes
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditNewDash;
|
||||
@@ -0,0 +1,381 @@
|
||||
// import React,{useState,useEffect} from 'react'
|
||||
// import { useForm, useFieldArray } from "react-hook-form";
|
||||
// import { useNavigate } from "react-router-dom";
|
||||
// import { toast } from "react-toastify";
|
||||
// import "react-toastify/dist/ReactToastify.css";
|
||||
// import { Badge } from "reactstrap";
|
||||
|
||||
// const DashboardbuilderAdd = () => {
|
||||
|
||||
// const [moduleId, setModuleId] = useState(null);
|
||||
// const [techStacks, setTechStacks] = useState([]);
|
||||
// const objectTypes = ["form", "bi", "report", "api"];
|
||||
// const subObjectTypes = [
|
||||
// "only header",
|
||||
// "only line",
|
||||
// "header line",
|
||||
// "header multiline",
|
||||
// "workflow",
|
||||
// "setup",
|
||||
// "std report",
|
||||
// "bi report",
|
||||
// "rest api",
|
||||
// ];
|
||||
|
||||
// const fieldModels = {
|
||||
// dashboard: [
|
||||
// {
|
||||
// cols: 4,
|
||||
// rows: 5,
|
||||
// x: 0,
|
||||
// y: 0,
|
||||
// name: "Radar Chart",
|
||||
// component: "Radar Chart",
|
||||
// },
|
||||
// ],
|
||||
// };
|
||||
|
||||
// const navigate = useNavigate();
|
||||
// const {
|
||||
// register,
|
||||
// handleSubmit,
|
||||
// control,
|
||||
// formState: { errors },
|
||||
// } = useForm({
|
||||
// defaultValues: {
|
||||
// dashboard_name: "",
|
||||
// description: "",
|
||||
// secuirity_profile: "",
|
||||
// module_id: null,
|
||||
// tech_Stack: "",
|
||||
// object_type: "",
|
||||
// sub_object_type: "",
|
||||
// add_to_home: true,
|
||||
// dashbord1_Line: [
|
||||
// {
|
||||
// model: JSON.stringify(fieldModels),
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// });
|
||||
|
||||
// const { fields } = useFieldArray({
|
||||
// control,
|
||||
// name: "dashbord1_Line",
|
||||
// });
|
||||
|
||||
// useEffect(() => {
|
||||
// // Simulate module ID fetching logic
|
||||
// setModuleId("exampleModuleId");
|
||||
// }, []);
|
||||
|
||||
// const onSubmit = (data) => {
|
||||
// console.log("Form Data:", data);
|
||||
// if (!data.dashboard_name || !data.secuirity_profile || !data.description) {
|
||||
// toast.error("Please fill out all required fields.");
|
||||
// return;
|
||||
// }
|
||||
|
||||
// data.module_id = moduleId;
|
||||
|
||||
// // DashboardService.create(data)
|
||||
// // .then((response) => {
|
||||
// // console.log(response);
|
||||
// // toast.success("Added successfully");
|
||||
// // navigate("../all");
|
||||
// // })
|
||||
// // .catch((error) => {
|
||||
// // console.error(error);
|
||||
// // toast.error("Error while adding dashboard");
|
||||
// // });
|
||||
// };
|
||||
|
||||
// const onValid = (data) => {
|
||||
// console.log('Form Submitted Successfully:', data);
|
||||
// };
|
||||
|
||||
// return (
|
||||
// <div className="container">
|
||||
// <h4 style={{ fontWeight: 300, display: "inline" }}>
|
||||
// <b>Define Dashboard</b>
|
||||
// </h4>
|
||||
// <Badge
|
||||
// color="info" // This sets the blue color
|
||||
// style={{ display: "inline", marginLeft: 10 }}
|
||||
// >
|
||||
// Add Mode
|
||||
// </Badge>
|
||||
// <hr />
|
||||
// <br />
|
||||
|
||||
// <form onSubmit={handleSubmit(onSubmit)}>
|
||||
// <div className="row">
|
||||
// <div className="col-md-6 col-sm-12">
|
||||
// <label htmlFor="dashboard_name">Dashboard Name</label>
|
||||
// <input
|
||||
// id="dashboard_name"
|
||||
// type="text"
|
||||
// {...register("dashboard_name", { required: true })}
|
||||
// className="form-control"
|
||||
// placeholder="Enter dashboard name"
|
||||
// />
|
||||
// {errors.dashboard_name && <p className="text-danger">This field is required.</p>}
|
||||
// </div>
|
||||
|
||||
// <div className="col-md-6 col-sm-12">
|
||||
// <label htmlFor="secuirity_profile">Security Profile</label>
|
||||
// <input
|
||||
// id="secuirity_profile"
|
||||
// type="text"
|
||||
// {...register("secuirity_profile", { required: true })}
|
||||
// className="form-control"
|
||||
// placeholder="Enter security profile"
|
||||
// />
|
||||
// {errors.secuirity_profile && <p className="text-danger">This field is required.</p>}
|
||||
// </div>
|
||||
|
||||
// <div className="col-md-6 col-sm-12">
|
||||
// <label htmlFor="description">Description</label>
|
||||
// <textarea
|
||||
// id="description"
|
||||
// {...register("description", { required: true })}
|
||||
// className="form-control"
|
||||
// placeholder="Enter description"
|
||||
// />
|
||||
// {errors.description && <p className="text-danger">This field is required.</p>}
|
||||
// </div>
|
||||
|
||||
// <div className="col-md-6 col-sm-12" >
|
||||
// <label htmlFor="add_to_home" style={{ marginTop: "1rem"}}>Add to Dashboard</label>
|
||||
// <input
|
||||
// type="checkbox"
|
||||
// {...register("add_to_home")}
|
||||
// className="form-check-input"
|
||||
// style={{marginTop:"3rem" , marginLeft:"-8rem"}}
|
||||
// />
|
||||
// </div>
|
||||
// </div>
|
||||
// <br />
|
||||
|
||||
// <div className="text-center">
|
||||
// <button
|
||||
// type="button"
|
||||
// className="btn btn-outline-secondary"
|
||||
// onClick={() => navigate("/admin/dashboard-new-all")}
|
||||
// >
|
||||
// Back
|
||||
// </button>
|
||||
// <button type="submit" className="btn btn-primary" onClick={handleSubmit()}>
|
||||
// Submit
|
||||
// </button>
|
||||
// </div>
|
||||
// </form>
|
||||
// </div>
|
||||
// )
|
||||
// }
|
||||
|
||||
// export default DashboardbuilderAdd
|
||||
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useForm, useFieldArray } from "react-hook-form";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { toast } from "react-toastify";
|
||||
import "react-toastify/dist/ReactToastify.css";
|
||||
import { Badge } from "reactstrap";
|
||||
import DashboardBuilderService from "../../../../APIServices/DashboardBuilderService";
|
||||
|
||||
const DashboardbuilderAdd = () => {
|
||||
const [moduleId, setModuleId] = useState(null);
|
||||
const [techStacks, setTechStacks] = useState([]);
|
||||
const objectTypes = ["form", "bi", "report", "api"];
|
||||
const subObjectTypes = [
|
||||
"only header",
|
||||
"only line",
|
||||
"header line",
|
||||
"header multiline",
|
||||
"workflow",
|
||||
"setup",
|
||||
"std report",
|
||||
"bi report",
|
||||
"rest api",
|
||||
];
|
||||
const [dashboards, setDashboards] = useState([]);
|
||||
|
||||
const fieldModels = {
|
||||
dashboard: [
|
||||
{
|
||||
cols: 4,
|
||||
rows: 5,
|
||||
x: 0,
|
||||
y: 0,
|
||||
name: "Radar Chart",
|
||||
component: "Radar Chart",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const navigate = useNavigate();
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
control,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
defaultValues: {
|
||||
dashboard_name: "",
|
||||
description: "",
|
||||
secuirity_profile: "",
|
||||
module_id: null,
|
||||
tech_Stack: "",
|
||||
object_type: "",
|
||||
sub_object_type: "",
|
||||
add_to_home: true,
|
||||
dashbord1_Line: [
|
||||
{
|
||||
model: JSON.stringify(fieldModels),
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
const { fields } = useFieldArray({
|
||||
control,
|
||||
name: "dashbord1_Line",
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate module ID fetching logic
|
||||
setModuleId("exampleModuleId");
|
||||
}, []);
|
||||
|
||||
// #############
|
||||
const onSubmit = (data) => {
|
||||
|
||||
console.log("Form Data:", data);
|
||||
if (!data.dashboard_name || !data.secuirity_profile || !data.description) {
|
||||
toast.error("Please fill out all required fields.");
|
||||
return;
|
||||
}
|
||||
|
||||
const newDashboard = {
|
||||
...data,
|
||||
// module_id: Number(moduleId), // Ensure this is an integer
|
||||
// id: Date.now(), // Simulate a unique ID
|
||||
};
|
||||
|
||||
console.log("New Dashboard:", newDashboard);
|
||||
const existingDashboards =
|
||||
JSON.parse(localStorage.getItem("dashboards")) || [];
|
||||
console.log("Existing Dashboards:", existingDashboards);
|
||||
|
||||
localStorage.setItem(
|
||||
"dashboards",
|
||||
JSON.stringify([...existingDashboards, newDashboard])
|
||||
);
|
||||
|
||||
setDashboards((prevDashboards) => [...prevDashboards, newDashboard]);
|
||||
|
||||
|
||||
|
||||
// API call to save the new dashboard
|
||||
DashboardBuilderService.create(newDashboard)
|
||||
.then((response) => {
|
||||
console.log(response);
|
||||
toast.success("Dashboard Added successfully");
|
||||
navigate("/admin/dashboard-new-all");
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
toast.error("Error while adding dashboard");
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
<h4 style={{ fontWeight: 300, display: "inline" }}>
|
||||
<b>Define Dashboard</b>
|
||||
</h4>
|
||||
<Badge
|
||||
color="info" // This sets the blue color
|
||||
style={{ display: "inline", marginLeft: 10 }}
|
||||
>
|
||||
Add Mode
|
||||
</Badge>
|
||||
<hr />
|
||||
<br />
|
||||
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="row">
|
||||
<div className="col-md-6 col-sm-12">
|
||||
<label htmlFor="dashboard_name">Dashboard Name</label>
|
||||
<input
|
||||
id="dashboard_name"
|
||||
type="text"
|
||||
{...register("dashboard_name", { required: true })}
|
||||
className="form-control"
|
||||
placeholder="Enter dashboard name"
|
||||
/>
|
||||
{errors.dashboard_name && (
|
||||
<p className="text-danger">This field is required.</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="col-md-6 col-sm-12">
|
||||
<label htmlFor="secuirity_profile">Security Profile</label>
|
||||
<input
|
||||
id="secuirity_profile"
|
||||
type="text"
|
||||
{...register("secuirity_profile", { required: true })}
|
||||
className="form-control"
|
||||
placeholder="Enter security profile"
|
||||
/>
|
||||
{errors.secuirity_profile && (
|
||||
<p className="text-danger">This field is required.</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="col-md-6 col-sm-12">
|
||||
<label htmlFor="description">Description</label>
|
||||
<textarea
|
||||
id="description"
|
||||
{...register("description", { required: true })}
|
||||
className="form-control"
|
||||
placeholder="Enter description"
|
||||
/>
|
||||
{errors.description && (
|
||||
<p className="text-danger">This field is required.</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="col-md-6 col-sm-12">
|
||||
<label htmlFor="add_to_home" style={{ marginTop: "1rem" }}>
|
||||
Add to Dashboard
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
{...register("add_to_home")}
|
||||
className="form-check-input"
|
||||
style={{ marginTop: "3rem", marginLeft: "-8rem" }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
|
||||
<div className="text-center">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-outline-secondary"
|
||||
onClick={() => navigate("/admin/dashboard-new-all")}
|
||||
>
|
||||
Back
|
||||
</button>
|
||||
<button type="submit" className="btn btn-primary">
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardbuilderAdd;
|
||||
@@ -0,0 +1,441 @@
|
||||
// import React, { useState } from "react";
|
||||
// import {
|
||||
// Button,
|
||||
// Modal,
|
||||
// Table,
|
||||
// Form,
|
||||
// Spinner,
|
||||
// Tooltip,
|
||||
// OverlayTrigger
|
||||
// } from "react-bootstrap";
|
||||
|
||||
// const DashboardNewAll = () => {
|
||||
// const [addModal, setAddModal] = useState(false);
|
||||
// const [deleteModal, setDeleteModal] = useState(false);
|
||||
// const [selectedRow, setSelectedRow] = useState(null);
|
||||
// const[data,setData] = useState([]);
|
||||
// const[loading,setLoading] = useState();
|
||||
// const [error,setError] = useState();
|
||||
// const[translate,setTranslate] = useState();
|
||||
|
||||
// const handleAddClick = () => setAddModal(true);
|
||||
// const handleDeleteClick = (row) => {
|
||||
// setSelectedRow(row);
|
||||
// setDeleteModal(true);
|
||||
// };
|
||||
|
||||
// const handleDeleteConfirm = () => {
|
||||
// console.log("Deleting row: ", selectedRow.id);
|
||||
// setDeleteModal(false);
|
||||
// };
|
||||
|
||||
// return (
|
||||
// <div className="dg-wrapper">
|
||||
// <div className="d-flex justify-content-between align-items-center mb-3">
|
||||
// <h3>{translate("Dashboard_builder")}</h3>
|
||||
// <div>
|
||||
// <Button variant="primary" onClick={() => console.log("Go to Runner")}>
|
||||
// <i className="bi bi-grid"></i> {translate("Dashboard_runner")}
|
||||
// </Button>
|
||||
// <Button
|
||||
// variant="outline-secondary"
|
||||
// className="mx-2"
|
||||
// onClick={() => console.log("Export")}
|
||||
// >
|
||||
// <i className="bi bi-file-earmark-excel"></i> {translate("EXPORT_XLSX")}
|
||||
// </Button>
|
||||
// <Button variant="primary" onClick={handleAddClick}>
|
||||
// <i className="bi bi-plus"></i> {translate("ADD")}
|
||||
// </Button>
|
||||
// </div>
|
||||
// </div>
|
||||
|
||||
// <Table striped bordered hover>
|
||||
// <thead>
|
||||
// <tr>
|
||||
// <th>{translate("Go_to")}</th>
|
||||
// <th>{translate("Dashboard_Name")}</th>
|
||||
// <th>{translate("Description")}</th>
|
||||
// <th>{translate("Security_Profile")}</th>
|
||||
// <th>{translate("Add_to_home")}</th>
|
||||
// <th>{translate("Action")}</th>
|
||||
// </tr>
|
||||
// </thead>
|
||||
// <tbody>
|
||||
// {loading ? (
|
||||
// <tr>
|
||||
// <td colSpan="6" className="text-center">
|
||||
// <Spinner animation="border" /> {translate("Loading")} ......
|
||||
// </td>
|
||||
// </tr>
|
||||
// ) : error ? (
|
||||
// <tr>
|
||||
// <td colSpan="6" className="text-center text-danger">
|
||||
// {error}
|
||||
// </td>
|
||||
// </tr>
|
||||
// ) : (
|
||||
// data?.slice()?.reverse()?.map((user) => (
|
||||
// <tr key={user.id}>
|
||||
// <td>
|
||||
// <span
|
||||
// className="badge bg-info text-dark"
|
||||
// style={{ cursor: "pointer" }}
|
||||
// onClick={() => console.log("Go to Edit", user.id)}
|
||||
// >
|
||||
// {translate("SET_UP")}
|
||||
// </span>
|
||||
// </td>
|
||||
// <td>{user.dashboard_name}</td>
|
||||
// <td>{user.description}</td>
|
||||
// <td>{user.security_profile}</td>
|
||||
// <td>{user.add_to_home}</td>
|
||||
// <td>
|
||||
// <OverlayTrigger
|
||||
// placement="top"
|
||||
// overlay={<Tooltip>{translate("Delete")}</Tooltip>}
|
||||
// >
|
||||
// <i
|
||||
// className="bi bi-trash text-danger"
|
||||
// style={{ cursor: "pointer" }}
|
||||
// onClick={() => handleDeleteClick(user)}
|
||||
// ></i>
|
||||
// </OverlayTrigger>
|
||||
// <OverlayTrigger
|
||||
// placement="top"
|
||||
// overlay={<Tooltip>{translate("Edit")}</Tooltip>}
|
||||
// >
|
||||
// <i
|
||||
// className="bi bi-pencil text-primary mx-2"
|
||||
// style={{ cursor: "pointer" }}
|
||||
// onClick={() => console.log("Edit", user.id)}
|
||||
// ></i>
|
||||
// </OverlayTrigger>
|
||||
// </td>
|
||||
// </tr>
|
||||
// ))
|
||||
// )}
|
||||
// </tbody>
|
||||
// </Table>
|
||||
|
||||
// {/* Add Modal */}
|
||||
// <Modal show={addModal} onHide={() => setAddModal(false)} size="lg" centered>
|
||||
// <Modal.Body>
|
||||
// <div className="d-flex justify-content-around">
|
||||
// <div
|
||||
// className="chart-box text-center"
|
||||
// onClick={() => console.log("Start from Scratch")}
|
||||
// >
|
||||
// <img
|
||||
// src="/assets/images/fromscratch.png"
|
||||
// alt="Start from Scratch"
|
||||
// height="90"
|
||||
// width="90"
|
||||
// />
|
||||
// <h5>{translate("Start_from_scratch")}</h5>
|
||||
// </div>
|
||||
// <div
|
||||
// className="chart-box text-center"
|
||||
// onClick={() => console.log("Import from Template")}
|
||||
// >
|
||||
// <img
|
||||
// src="/assets/images/copytemplate.png"
|
||||
// alt="Import from Template"
|
||||
// height="90"
|
||||
// width="90"
|
||||
// />
|
||||
// <h5>{translate("Import_from_template")}</h5>
|
||||
// </div>
|
||||
// <div
|
||||
// className="chart-box text-center"
|
||||
// onClick={() => console.log("Import from Public Project")}
|
||||
// >
|
||||
// <img
|
||||
// src="/assets/images/database.png"
|
||||
// alt="Import from Public Project"
|
||||
// height="90"
|
||||
// width="90"
|
||||
// />
|
||||
// <h5>{translate("Import_from_public_project")}</h5>
|
||||
// </div>
|
||||
// </div>
|
||||
// </Modal.Body>
|
||||
// </Modal>
|
||||
|
||||
// {/* Delete Modal */}
|
||||
// <Modal show={deleteModal} onHide={() => setDeleteModal(false)} centered>
|
||||
// <Modal.Body>
|
||||
// <h1 className="text-danger text-center">
|
||||
// {translate("Are_you_sure_want_to_delete")}
|
||||
// </h1>
|
||||
// <h2 className="text-center">{selectedRow?.id}</h2>
|
||||
// <div className="d-flex justify-content-around mt-4">
|
||||
// <Button variant="outline-secondary" onClick={() => setDeleteModal(false)}>
|
||||
// {translate("Cancel")}
|
||||
// </Button>
|
||||
// <Button variant="primary" onClick={handleDeleteConfirm}>
|
||||
// {translate("Delete")}
|
||||
// </Button>
|
||||
// </div>
|
||||
// </Modal.Body>
|
||||
// </Modal>
|
||||
// </div>
|
||||
// );
|
||||
// };
|
||||
|
||||
// export default DashboardNewAll;
|
||||
|
||||
|
||||
import React, { useState, useEffect } from "react";
|
||||
import {
|
||||
Button,
|
||||
Modal,
|
||||
Table,
|
||||
Spinner,
|
||||
Tooltip,
|
||||
OverlayTrigger,
|
||||
} from "react-bootstrap";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useNavigate, useLocation } from "react-router-dom";
|
||||
import * as XLSX from "xlsx";
|
||||
import { toast } from "react-toastify";
|
||||
import ConfirmModal from "../../../common/ConfirmModal"
|
||||
import DashboardBuilderService from "../../../../APIServices/DashboardBuilderService";
|
||||
|
||||
const DashboardNewAll = () => {
|
||||
const { t: translate } = useTranslation(); // Use `translate` as the translation function
|
||||
const [addModal, setAddModal] = useState(false);
|
||||
const [showConfirmModal, setShowConfirmModal] = useState(false);
|
||||
const [deleteModal, setDeleteModal] = useState(false);
|
||||
const [selectedRow, setSelectedRow] = useState(null);
|
||||
const [data, setData] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState(null);
|
||||
const [refresh, setRefresh] = useState(false);
|
||||
const [tableData, setTableData] = useState([]);
|
||||
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
const storedDashboards = JSON.parse(localStorage.getItem("dashboards")) || [];
|
||||
setData(storedDashboards);
|
||||
console.log("Stored Dashboards:", storedDashboards);
|
||||
|
||||
if (location.state?.updatedDashboard) {
|
||||
const updatedDashboard = location.state.updatedDashboard;
|
||||
setData(prevData => prevData.map(dashboard =>
|
||||
dashboard.id === updatedDashboard.id ? updatedDashboard : dashboard
|
||||
));
|
||||
location.state.updatedDashboard = null;
|
||||
}
|
||||
setLoading(false);
|
||||
}, [refresh, location.state?.updatedDashboard]);
|
||||
|
||||
const handleAddClick = () => {
|
||||
navigate("/admin/dashboard-new-add");
|
||||
}
|
||||
const handleDeleteClick = (row) => {
|
||||
setSelectedRow(row);
|
||||
setShowConfirmModal(true);
|
||||
};
|
||||
|
||||
const handleDeleteConfirm = async () => {
|
||||
try {
|
||||
// Call the delete API
|
||||
await DashboardBuilderService.deleteField(selectedRow.id);
|
||||
|
||||
// Update the UI after successful deletion
|
||||
setData((prevData) => prevData.filter((dashboard) => dashboard.id !== selectedRow.id));
|
||||
toast.success("Dashboard deleted successfully!");
|
||||
} catch (error) {
|
||||
console.error("Error deleting dashboard field:", error);
|
||||
toast.error("Failed to delete dashboard. Please try again.");
|
||||
}
|
||||
};
|
||||
const handleRunner = () => {
|
||||
console.log("Go to Runner");
|
||||
navigate("/admin/dashboard-runner-all");
|
||||
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
fetchAllDashboards();
|
||||
}, []);
|
||||
|
||||
const fetchAllDashboards = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const dashboards = await DashboardBuilderService.getAllDash(); // Call service
|
||||
console.log('Fetched Dashboards:', dashboards); // Verify data
|
||||
setData(dashboards); // Update state with the array
|
||||
toast.success("Dashboards fetched successfully.");
|
||||
} catch (error) {
|
||||
console.error('Error fetching dashboards:', error);
|
||||
toast.error('Failed to fetch dashboards.');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleEditClick = (dashboard) => {
|
||||
navigate(`/admin/dashboard-new-edit/${dashboard.id}`, { state: { dashboard } }); // Pass the ID in the URL
|
||||
}
|
||||
|
||||
const handleExport = () => {
|
||||
console.log("Export to Excel");
|
||||
// Create a new workbook and a worksheet
|
||||
const workbook = XLSX.utils.book_new();
|
||||
const worksheet = XLSX.utils.json_to_sheet(data);
|
||||
|
||||
// Append the worksheet to the workbook
|
||||
XLSX.utils.book_append_sheet(workbook, worksheet, "Sheet1");
|
||||
|
||||
// Write the workbook and download the file
|
||||
XLSX.writeFile(workbook, "exported_data.xlsx");
|
||||
}
|
||||
|
||||
const handleSetUp = (id) => {
|
||||
console.log("dashboard id : ", id)
|
||||
navigate(`/admin/edit-new-dash/${id}`,{dashboardId:id});
|
||||
}
|
||||
const refreshData = () => {
|
||||
setRefresh((prev) => !prev); // Toggle the refresh state to trigger re-fetch
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="dg-wrapper">
|
||||
<div className="d-flex justify-content-between align-items-center mb-3">
|
||||
<h3>{translate("Dashboard Builder")}</h3>
|
||||
<div>
|
||||
<Button variant="primary" onClick={() => handleRunner()}>
|
||||
<i className="bi bi-grid"></i> {translate("Dashboard_runner")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline-secondary"
|
||||
className="mx-2"
|
||||
onClick={() => handleExport()}
|
||||
>
|
||||
<i className="bi bi-file-earmark-excel"></i> {translate("EXPORT_XLSX")}
|
||||
</Button>
|
||||
<Button variant="primary" onClick={handleAddClick}>
|
||||
<i className="bi bi-plus"></i> {translate("ADD")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<Table striped bordered hover>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Go To</th>
|
||||
<th>Dashboard Name</th>
|
||||
<th>Description</th>
|
||||
<th>Security Profile</th>
|
||||
<th>Add to Home</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{loading ? (
|
||||
<tr>
|
||||
<td colSpan="6" className="text-center">
|
||||
<Spinner animation="border" /> Loading...
|
||||
</td>
|
||||
</tr>
|
||||
) : data.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan="6" className="text-center">No dashboards available.</td>
|
||||
</tr>
|
||||
) : (
|
||||
data.map((dashboard) => (
|
||||
<tr key={dashboard.id}>
|
||||
<td>
|
||||
<span
|
||||
className="badge bg-info text-dark"
|
||||
style={{ cursor: "pointer" }}
|
||||
onClick={() => handleSetUp(dashboard.id)}
|
||||
>
|
||||
SET UP
|
||||
</span>
|
||||
</td>
|
||||
<td>{dashboard.dashboard_name}</td>
|
||||
<td>{dashboard.description}</td>
|
||||
<td>{dashboard.secuirity_profile}</td>
|
||||
<td>{dashboard.add_to_home ? "Yes" : "No"}</td>
|
||||
<td>
|
||||
<i
|
||||
className="bi bi-trash text-danger"
|
||||
style={{ cursor: "pointer" }}
|
||||
onClick={() => handleDeleteClick(dashboard)}
|
||||
></i>
|
||||
<i
|
||||
className="bi bi-pencil text-primary mx-2"
|
||||
style={{ cursor: "pointer" }}
|
||||
onClick={() => handleEditClick(dashboard)}
|
||||
></i>
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
)}
|
||||
</tbody>
|
||||
</Table>
|
||||
{/* Add Modal */}
|
||||
<Modal show={addModal} onHide={() => setAddModal(false)} size="lg" centered>
|
||||
<Modal.Body>
|
||||
<div className="d-flex justify-content-around">
|
||||
<div
|
||||
className="chart-box text-center"
|
||||
onClick={() => console.log("Start from Scratch")}
|
||||
>
|
||||
<img
|
||||
src="/assets/images/fromscratch.png"
|
||||
alt="Start from Scratch"
|
||||
height="90"
|
||||
width="90"
|
||||
/>
|
||||
<h5>{translate("Start_from_scratch")}</h5>
|
||||
</div>
|
||||
<div
|
||||
className="chart-box text-center"
|
||||
onClick={() => console.log("Import from Template")}
|
||||
>
|
||||
<img
|
||||
src="/assets/images/copytemplate.png"
|
||||
alt="Import from Template"
|
||||
height="90"
|
||||
width="90"
|
||||
/>
|
||||
<h5>{translate("Import_from_template")}</h5>
|
||||
</div>
|
||||
<div
|
||||
className="chart-box text-center"
|
||||
onClick={() => console.log("Import from Public Project")}
|
||||
>
|
||||
<img
|
||||
src="/assets/images/database.png"
|
||||
alt="Import from Public Project"
|
||||
height="90"
|
||||
width="90"
|
||||
/>
|
||||
<h5>{translate("Import_from_public_project")}</h5>
|
||||
</div>
|
||||
</div>
|
||||
</Modal.Body>
|
||||
</Modal>
|
||||
|
||||
{/* Confirmation Modal */}
|
||||
<ConfirmModal
|
||||
show={showConfirmModal}
|
||||
onHide={() => setShowConfirmModal(false)}
|
||||
onConfirm={handleDeleteConfirm}
|
||||
title="Delete Dashboard"
|
||||
message={`Are you sure you want to delete "${selectedRow?.dashboard_name || 'this dashboard'}"? This action cannot be undone.`}
|
||||
confirmLabel="Delete"
|
||||
cancelLabel="Cancel"
|
||||
variant="danger"
|
||||
/>
|
||||
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardNewAll;
|
||||
@@ -0,0 +1,76 @@
|
||||
import React from 'react';
|
||||
import { Bar } from 'react-chartjs-2';
|
||||
import {
|
||||
Chart as ChartJS,
|
||||
CategoryScale,
|
||||
LinearScale,
|
||||
BarElement,
|
||||
Title,
|
||||
Tooltip,
|
||||
Legend,
|
||||
} from 'chart.js';
|
||||
|
||||
|
||||
|
||||
const BarChart = () => {
|
||||
const barChartLabels = ['Apple', 'Banana', 'Kiwifruit', 'Blueberry', 'Orange', 'Grapes'];
|
||||
const barChartData = {
|
||||
labels: barChartLabels,
|
||||
datasets: [
|
||||
{
|
||||
label: 'Best Fruits',
|
||||
data: [45, 37, 60, 70, 46, 33],
|
||||
backgroundColor: 'rgba(75, 192, 192, 0.2)',
|
||||
borderColor: 'rgba(75, 192, 192, 1)',
|
||||
borderWidth: 1,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const options = {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: true,
|
||||
position: 'top',
|
||||
},
|
||||
tooltip: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
beginAtZero: true,
|
||||
},
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const handleChartClick = (event, elements) => {
|
||||
if (elements.length > 0) {
|
||||
const { index } = elements[0];
|
||||
console.log(`Clicked on ${barChartLabels[index]}`);
|
||||
}
|
||||
};
|
||||
|
||||
const handleChartHover = (event, elements) => {
|
||||
if (elements.length > 0) {
|
||||
console.log('Hovered on chart element:', elements);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ display: 'block', width: '600px', margin: 'auto' }}>
|
||||
<Bar
|
||||
data={barChartData}
|
||||
options={options}
|
||||
onClick={(event, elements) => handleChartClick(event, elements)}
|
||||
onHover={(event, elements) => handleChartHover(event, elements)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default BarChart;
|
||||
@@ -0,0 +1,92 @@
|
||||
// BubbleChart.js
|
||||
import React from 'react';
|
||||
import { Bubble } from 'react-chartjs-2';
|
||||
import {
|
||||
Chart as ChartJS,
|
||||
LinearScale,
|
||||
PointElement,
|
||||
Tooltip,
|
||||
Legend,
|
||||
Title,
|
||||
} from 'chart.js';
|
||||
|
||||
// ChartJS.register(LinearScale, PointElement, Tooltip, Legend, Title);
|
||||
|
||||
const BubbleChart = () => {
|
||||
const bubbleChartOptions = {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Bubble Chart',
|
||||
},
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
min: 0,
|
||||
max: 30,
|
||||
},
|
||||
y: {
|
||||
min: 0,
|
||||
max: 30,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const bubbleChartData = {
|
||||
datasets: [
|
||||
{
|
||||
data: [
|
||||
{ x: 10, y: 10, r: 10 },
|
||||
{ x: 15, y: 5, r: 15 },
|
||||
{ x: 26, y: 12, r: 23 },
|
||||
{ x: 7, y: 8, r: 8 },
|
||||
],
|
||||
label: 'Investment Equities',
|
||||
backgroundColor: 'rgba(255, 0, 0, 0.6)', // Red
|
||||
borderColor: 'blue',
|
||||
hoverBackgroundColor: 'purple',
|
||||
hoverBorderColor: 'red',
|
||||
},
|
||||
{
|
||||
data: [
|
||||
{ x: 5, y: 15, r: 12 },
|
||||
{ x: 20, y: 7, r: 8 },
|
||||
{ x: 12, y: 18, r: 15 },
|
||||
{ x: 8, y: 6, r: 10 },
|
||||
],
|
||||
label: 'Investment Bonds',
|
||||
backgroundColor: 'rgba(0, 255, 0, 0.6)', // Green
|
||||
borderColor: 'green',
|
||||
hoverBackgroundColor: 'yellow',
|
||||
hoverBorderColor: 'blue',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const handleChartClick = (event, elements) => {
|
||||
if (elements.length > 0) {
|
||||
const { datasetIndex, index } = elements[0].element.$context;
|
||||
console.log(`Clicked on:`, bubbleChartData.datasets[datasetIndex].data[index]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleChartHover = (event, elements) => {
|
||||
if (elements.length > 0) {
|
||||
console.log('Hovered over:', elements);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ display: 'block', width: '600px', margin: 'auto' }}>
|
||||
<Bubble
|
||||
options={bubbleChartOptions}
|
||||
data={bubbleChartData}
|
||||
onClick={(event, elements) => handleChartClick(event, elements)}
|
||||
onHover={(event, elements) => handleChartHover(event, elements)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default BubbleChart;
|
||||
@@ -0,0 +1,72 @@
|
||||
// DoughnutChart.js
|
||||
import React from 'react';
|
||||
import { Doughnut } from 'react-chartjs-2';
|
||||
import {
|
||||
Chart as ChartJS,
|
||||
ArcElement,
|
||||
Tooltip,
|
||||
Legend,
|
||||
} from 'chart.js';
|
||||
|
||||
//
|
||||
|
||||
const DoughnutChart = () => {
|
||||
const doughnutChartData = {
|
||||
labels: ["Download Sales", "In-Store Sales", "Mail-Order Sales"],
|
||||
datasets: [
|
||||
{
|
||||
data: [350, 450, 100],
|
||||
backgroundColor: [
|
||||
'rgba(255, 99, 132, 0.6)',
|
||||
'rgba(54, 162, 235, 0.6)',
|
||||
'rgba(255, 206, 86, 0.6)'
|
||||
],
|
||||
borderColor: [
|
||||
'rgba(255, 99, 132, 1)',
|
||||
'rgba(54, 162, 235, 1)',
|
||||
'rgba(255, 206, 86, 1)'
|
||||
],
|
||||
borderWidth: 1,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const doughnutChartOptions = {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'top',
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Doughnut Chart',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const handleChartClick = (event, elements) => {
|
||||
if (elements.length > 0) {
|
||||
const { index } = elements[0].element.$context;
|
||||
console.log(`Clicked on:`, doughnutChartData.labels[index]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleChartHover = (event, elements) => {
|
||||
if (elements.length > 0) {
|
||||
console.log('Hovered over:', elements);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ display: 'block', width: '600px', margin: 'auto' }}>
|
||||
<Doughnut
|
||||
data={doughnutChartData}
|
||||
options={doughnutChartOptions}
|
||||
onClick={(event, elements) => handleChartClick(event, elements)}
|
||||
onHover={(event, elements) => handleChartHover(event, elements)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DoughnutChart;
|
||||
@@ -0,0 +1,79 @@
|
||||
// DynamicChart.js
|
||||
import React, { useState } from 'react';
|
||||
import { Bar } from 'react-chartjs-2';
|
||||
import {
|
||||
Chart as ChartJS,
|
||||
CategoryScale,
|
||||
LinearScale,
|
||||
BarElement,
|
||||
Title,
|
||||
Tooltip,
|
||||
Legend,
|
||||
} from 'chart.js';
|
||||
|
||||
//
|
||||
|
||||
const DynamicChart = () => {
|
||||
const [chartType, setChartType] = useState('bar');
|
||||
|
||||
const barChartOptions = {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: true,
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Dynamic Chart',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const dynamicChartLabels = ['2006', '2007', '2008', '2009', '2010', '2011', '2012'];
|
||||
|
||||
const dynamicChartData = {
|
||||
labels: dynamicChartLabels,
|
||||
datasets: [
|
||||
{ data: [65, 59, 90, 81, 56, 55, 40], label: 'Series A', backgroundColor: 'rgba(75, 192, 192, 0.6)' },
|
||||
{ data: [28, 48, 40, 19, 96, 27, 100], label: 'Series B', backgroundColor: 'rgba(153, 102, 255, 0.6)' },
|
||||
],
|
||||
};
|
||||
|
||||
const handleChartClick = (event, elements) => {
|
||||
if (elements.length > 0) {
|
||||
const { index } = elements[0].element.$context;
|
||||
console.log(`Clicked on:`, dynamicChartLabels[index]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleChartHover = (event, elements) => {
|
||||
if (elements.length > 0) {
|
||||
console.log('Hovered over:', elements);
|
||||
}
|
||||
};
|
||||
|
||||
const randomize = () => {
|
||||
setChartType((prevType) => (prevType === 'bar' ? 'line' : 'bar'));
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ display: 'block', width: '800px', margin: 'auto' }}>
|
||||
<Bar
|
||||
data={dynamicChartData}
|
||||
options={barChartOptions}
|
||||
type={chartType}
|
||||
onClick={(event, elements) => handleChartClick(event, elements)}
|
||||
onHover={(event, elements) => handleChartHover(event, elements)}
|
||||
/>
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
style={{ marginTop: '20px' }}
|
||||
onClick={randomize}
|
||||
>
|
||||
Update
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DynamicChart;
|
||||
@@ -0,0 +1,78 @@
|
||||
import React from 'react';
|
||||
import { Line } from 'react-chartjs-2';
|
||||
import {
|
||||
Chart as ChartJS,
|
||||
CategoryScale,
|
||||
LinearScale,
|
||||
PointElement,
|
||||
LineElement,
|
||||
Title,
|
||||
Tooltip,
|
||||
Legend,
|
||||
} from 'chart.js';
|
||||
|
||||
// ✅ Register Chart.js components
|
||||
ChartJS.register(CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend);
|
||||
|
||||
// ✅ Default stock data (used if no data is passed)
|
||||
const defaultStockData = [
|
||||
{ date: '2024-03-01', price: 120 },
|
||||
{ date: '2024-03-02', price: 125 },
|
||||
{ date: '2024-03-03', price: 130 },
|
||||
{ date: '2024-03-04', price: 128 },
|
||||
{ date: '2024-03-05', price: 135 },
|
||||
];
|
||||
|
||||
const FinancialChart = ({ data }) => {
|
||||
// ✅ Use default data if no data is provided
|
||||
const stockData = data && data.length > 0 ? data : defaultStockData;
|
||||
|
||||
const chartData = {
|
||||
labels: stockData.map(item => item.date), // X-axis labels (dates)
|
||||
datasets: [
|
||||
{
|
||||
label: 'Stock Price',
|
||||
data: stockData.map(item => item.price), // Y-axis values (prices)
|
||||
borderColor: 'rgba(75, 192, 192, 1)',
|
||||
backgroundColor: 'rgba(75, 192, 192, 0.2)',
|
||||
fill: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const chartOptions = {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Financial Data Chart',
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: (tooltipItem) => `$${tooltipItem.raw.toFixed(2)}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Date',
|
||||
},
|
||||
},
|
||||
y: {
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Price (USD)',
|
||||
},
|
||||
ticks: {
|
||||
callback: (value) => `$${value.toFixed(2)}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return <Line data={chartData} options={chartOptions} />;
|
||||
};
|
||||
|
||||
export default FinancialChart;
|
||||
@@ -0,0 +1,102 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Table, Spinner, Pagination, Form } from "react-bootstrap";
|
||||
import {toast} from "react-toastify"; // if you want to use Toast notifications, or use a React version of it
|
||||
import moment from "moment";
|
||||
// import { ExcelService } from "src/app/services/excel.service";
|
||||
// import { UsergrpmaintainceService } from "src/app/services/admin/usergrpmaintaince.service";
|
||||
// import { MenuGroupService } from "src/app/services/admin/menu-group.service";
|
||||
|
||||
const GridViewComponent = () => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [givendata, setGivendata] = useState([]);
|
||||
const [error, setError] = useState(null);
|
||||
const [modalAdd, setModalAdd] = useState(false);
|
||||
const [modalEdit, setModalEdit] = useState(false);
|
||||
const [modalDelete, setModalDelete] = useState(false);
|
||||
const [rowSelected, setRowSelected] = useState({});
|
||||
const [orders, setOrders] = useState([]);
|
||||
const [submitted, setSubmitted] = useState(false);
|
||||
|
||||
// useEffect(() => {
|
||||
// // Fetching the data on component mount
|
||||
// setLoading(true);
|
||||
// UsergrpmaintainceService.getAll()
|
||||
// .then((data) => {
|
||||
// setLoading(false);
|
||||
// setGivendata(data);
|
||||
// if (data.length === 0) {
|
||||
// setError("No data Available");
|
||||
// }
|
||||
// })
|
||||
// .catch((err) => {
|
||||
// setLoading(false);
|
||||
// setError("Server Error");
|
||||
// });
|
||||
// }, []);
|
||||
|
||||
const handleRowSelect = (row) => {
|
||||
setRowSelected(row);
|
||||
};
|
||||
|
||||
const handlePagination = (page) => {
|
||||
// Handle pagination logic here
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ display: "block" }}>
|
||||
<div className="dg-wrapper">
|
||||
<div className="clr-row">
|
||||
<div className="clr-col-8">
|
||||
<h3>User Group Maintenance</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{loading ? (
|
||||
<div>
|
||||
<Spinner animation="border" /> Loading...
|
||||
</div>
|
||||
) : error ? (
|
||||
<div>{error}</div>
|
||||
) : (
|
||||
<Table striped bordered hover>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>User Group No</th>
|
||||
<th>Group Name</th>
|
||||
<th>Description</th>
|
||||
<th>Group Level</th>
|
||||
<th>Status</th>
|
||||
<th>Updated Date</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{givendata.reverse().map((user, index) => (
|
||||
<tr key={index} onClick={() => handleRowSelect(user)}>
|
||||
<td>{user.usrGrp}</td>
|
||||
<td>{user.groupName}</td>
|
||||
<td>{user.groupDesc}</td>
|
||||
<td>{user.groupLevel}</td>
|
||||
<td>{user.status}</td>
|
||||
<td>{user.updateDateFormated}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</Table>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{/* Pagination logic */}
|
||||
<Pagination>
|
||||
{/* Add pagination items here */}
|
||||
<Pagination.Item onClick={() => handlePagination(1)}>1</Pagination.Item>
|
||||
<Pagination.Item onClick={() => handlePagination(2)}>2</Pagination.Item>
|
||||
{/* Pagination logic based on the fetched data */}
|
||||
</Pagination>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default GridViewComponent;
|
||||
@@ -0,0 +1,236 @@
|
||||
// import React, { useState, useEffect } from 'react';
|
||||
// import { Line } from 'react-chartjs-2'; // Assuming you're using react-chartjs-2 for the chart rendering
|
||||
// import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend } from 'chart.js';
|
||||
|
||||
// //
|
||||
|
||||
// const LineChartComponent = () => {
|
||||
// const [lineChartData, setLineChartData] = useState([
|
||||
// { data: [65, 59, 80, 81, 56, 55, 40], label: 'Series A' },
|
||||
// { data: [28, 48, 40, 19, 86, 27, 90], label: 'Series B' },
|
||||
// { data: [18, 48, 77, 9, 100, 27, 40], label: 'Series C' },
|
||||
// ]);
|
||||
|
||||
// const [lineChartLabels] = useState(['January', 'February', 'March', 'April', 'May', 'June', 'July']);
|
||||
// const [lineChartOptions] = useState({
|
||||
// responsive: true,
|
||||
// });
|
||||
|
||||
// const [lineChartColors] = useState([
|
||||
// {
|
||||
// backgroundColor: 'rgba(148,159,177,0.2)',
|
||||
// borderColor: 'rgba(148,159,177,1)',
|
||||
// pointBackgroundColor: 'rgba(148,159,177,1)',
|
||||
// pointBorderColor: '#fff',
|
||||
// pointHoverBackgroundColor: '#fff',
|
||||
// pointHoverBorderColor: 'rgba(148,159,177,0.8)',
|
||||
// },
|
||||
// {
|
||||
// backgroundColor: 'rgba(77,83,96,0.2)',
|
||||
// borderColor: 'rgba(77,83,96,1)',
|
||||
// pointBackgroundColor: 'rgba(77,83,96,1)',
|
||||
// pointBorderColor: '#fff',
|
||||
// pointHoverBackgroundColor: '#fff',
|
||||
// pointHoverBorderColor: 'rgba(77,83,96,1)',
|
||||
// },
|
||||
// {
|
||||
// backgroundColor: 'rgba(148,159,177,0.2)',
|
||||
// borderColor: 'rgba(148,159,177,1)',
|
||||
// pointBackgroundColor: 'rgba(148,159,177,1)',
|
||||
// pointBorderColor: '#fff',
|
||||
// pointHoverBackgroundColor: '#fff',
|
||||
// pointHoverBorderColor: 'rgba(148,159,177,0.8)',
|
||||
// },
|
||||
// ]);
|
||||
|
||||
// const [lineChartLegend] = useState(true);
|
||||
// const [lineChartType] = useState('line');
|
||||
|
||||
// // Function to randomize chart data
|
||||
// const randomize = () => {
|
||||
// const _lineChartData = lineChartData.map((series) => ({
|
||||
// ...series,
|
||||
// data: series.data.map(() => Math.floor(Math.random() * 100) + 1),
|
||||
// }));
|
||||
// setLineChartData(_lineChartData);
|
||||
// };
|
||||
|
||||
// // Chart hover and click events
|
||||
// const chartClicked = (e) => {
|
||||
// console.log(e);
|
||||
// };
|
||||
|
||||
// const chartHovered = (e) => {
|
||||
// console.log(e);
|
||||
// };
|
||||
|
||||
// return (
|
||||
// <div style={{ display: 'block' }}>
|
||||
// <Line
|
||||
// data={{
|
||||
// labels: lineChartLabels,
|
||||
// datasets: lineChartData,
|
||||
// }}
|
||||
// options={lineChartOptions}
|
||||
// legend={lineChartLegend}
|
||||
// type={lineChartType}
|
||||
// onHover={chartHovered}
|
||||
// onClick={chartClicked}
|
||||
// />
|
||||
// {/* You can add a button here to call randomize() */}
|
||||
// <button onClick={randomize}>Randomize Data</button>
|
||||
// </div>
|
||||
// );
|
||||
// };
|
||||
|
||||
// export default LineChartComponent;
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
Chart as ChartJS,
|
||||
CategoryScale,
|
||||
LinearScale,
|
||||
PointElement,
|
||||
LineElement,
|
||||
Title,
|
||||
Tooltip,
|
||||
Legend
|
||||
} from 'chart.js';
|
||||
import { Line } from 'react-chartjs-2';
|
||||
import { Box, Button } from '@mui/material';
|
||||
|
||||
// Register Chart.js components
|
||||
ChartJS.register(
|
||||
CategoryScale,
|
||||
LinearScale,
|
||||
PointElement,
|
||||
LineElement,
|
||||
Title,
|
||||
Tooltip,
|
||||
Legend
|
||||
);
|
||||
|
||||
const LineChartComponent = ({
|
||||
chartTitle = 'Line Chart',
|
||||
showLegend = true,
|
||||
showLabel = true,
|
||||
xAxis = 'Category',
|
||||
yAxis = 'Value',
|
||||
width = '100%',
|
||||
height = '100%'
|
||||
}) => {
|
||||
const [lineChartData, setLineChartData] = useState({
|
||||
labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
|
||||
datasets: [
|
||||
{
|
||||
label: 'Series A',
|
||||
data: [65, 59, 80, 81, 56, 55, 40],
|
||||
backgroundColor: 'rgba(75, 192, 192, 0.2)',
|
||||
borderColor: 'rgba(75, 192, 192, 1)',
|
||||
borderWidth: 2,
|
||||
pointBackgroundColor: 'rgba(75, 192, 192, 1)',
|
||||
pointBorderColor: '#fff',
|
||||
pointHoverBackgroundColor: '#fff',
|
||||
pointHoverBorderColor: 'rgba(75, 192, 192, 1)',
|
||||
tension: 0.1
|
||||
},
|
||||
{
|
||||
label: 'Series B',
|
||||
data: [28, 48, 40, 19, 86, 27, 90],
|
||||
backgroundColor: 'rgba(54, 162, 235, 0.2)',
|
||||
borderColor: 'rgba(54, 162, 235, 1)',
|
||||
borderWidth: 2,
|
||||
pointBackgroundColor: 'rgba(54, 162, 235, 1)',
|
||||
pointBorderColor: '#fff',
|
||||
pointHoverBackgroundColor: '#fff',
|
||||
pointHoverBorderColor: 'rgba(54, 162, 235, 1)',
|
||||
tension: 0.1
|
||||
},
|
||||
{
|
||||
label: 'Series C',
|
||||
data: [18, 48, 77, 9, 100, 27, 40],
|
||||
backgroundColor: 'rgba(255, 99, 132, 0.2)',
|
||||
borderColor: 'rgba(255, 99, 132, 1)',
|
||||
borderWidth: 2,
|
||||
pointBackgroundColor: 'rgba(255, 99, 132, 1)',
|
||||
pointBorderColor: '#fff',
|
||||
pointHoverBackgroundColor: '#fff',
|
||||
pointHoverBorderColor: 'rgba(255, 99, 132, 1)',
|
||||
tension: 0.1
|
||||
},
|
||||
]
|
||||
});
|
||||
|
||||
// Options for the chart
|
||||
const options = {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: showLegend,
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: chartTitle,
|
||||
},
|
||||
tooltip: {
|
||||
enabled: true,
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
title: {
|
||||
display: showLabel,
|
||||
text: xAxis
|
||||
}
|
||||
},
|
||||
y: {
|
||||
title: {
|
||||
display: showLabel,
|
||||
text: yAxis
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Function to randomize chart data
|
||||
const randomize = () => {
|
||||
setLineChartData(prevData => ({
|
||||
...prevData,
|
||||
datasets: prevData.datasets.map(dataset => ({
|
||||
...dataset,
|
||||
data: Array.from({ length: 7 }, () => Math.floor(Math.random() * 100) + 1)
|
||||
}))
|
||||
}));
|
||||
};
|
||||
|
||||
return (
|
||||
<Box sx={{
|
||||
width,
|
||||
height,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
position: 'relative'
|
||||
}}>
|
||||
<Box sx={{ flex: 1, minHeight: 0 }}>
|
||||
<Line data={lineChartData} options={options} />
|
||||
</Box>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
pt: 1
|
||||
}}>
|
||||
<Button
|
||||
variant="outlined"
|
||||
size="small"
|
||||
onClick={randomize}
|
||||
sx={{ fontSize: '0.75rem' }}
|
||||
>
|
||||
Randomize Data
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default LineChartComponent;
|
||||
@@ -0,0 +1,42 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Pie } from 'react-chartjs-2'; // Import Pie chart from react-chartjs-2
|
||||
import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js';
|
||||
|
||||
// Register required elements for Pie chart
|
||||
|
||||
const PieChartComponent = () => {
|
||||
// Define pie chart data and labels
|
||||
const [pieChartData] = useState([30, 50, 20]);
|
||||
const [pieChartLabels] = useState(['SciFi', 'Drama', 'Comedy']);
|
||||
const [pieChartType] = useState('pie');
|
||||
|
||||
// Handle chart hover and click events
|
||||
const chartClicked = (e) => {
|
||||
console.log(e);
|
||||
};
|
||||
|
||||
const chartHovered = (e) => {
|
||||
console.log(e);
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ display: 'block' }}>
|
||||
<Pie
|
||||
data={{
|
||||
labels: pieChartLabels,
|
||||
datasets: [
|
||||
{
|
||||
data: pieChartData,
|
||||
backgroundColor: ['#FF6384', '#36A2EB', '#FFCE56'], // Customize the colors
|
||||
},
|
||||
],
|
||||
}}
|
||||
type={pieChartType}
|
||||
onHover={chartHovered}
|
||||
onClick={chartClicked}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PieChartComponent;
|
||||
@@ -0,0 +1,125 @@
|
||||
// import React, { useState } from 'react';
|
||||
// import { Polar } from 'react-chartjs-2'; // Import PolarArea chart from react-chartjs-2
|
||||
// import { Chart as ChartJS, PolarAreaElement, Tooltip, Legend } from 'chart.js';
|
||||
|
||||
// ChartJS.register(PolarAreaElement, Tooltip, Legend); // Register the required elements for Polar Area chart
|
||||
|
||||
// const PolarChartComponent = () => {
|
||||
// // Define polar area chart data, labels, and type
|
||||
// const [polarAreaChartLabels] = useState([
|
||||
// 'Download Sales',
|
||||
// 'In-Store Sales',
|
||||
// 'Mail Sales',
|
||||
// 'Telesales',
|
||||
// 'Corporate Sales'
|
||||
// ]);
|
||||
// const [polarAreaChartData] = useState([
|
||||
// { data: [300, 500, 100, 40, 120], label: 'Series 1' }
|
||||
// ]);
|
||||
// const [polarAreaChartType] = useState('polarArea');
|
||||
|
||||
// // Handle chart hover and click events
|
||||
// const chartClicked = (e) => {
|
||||
// console.log(e);
|
||||
// };
|
||||
|
||||
// const chartHovered = (e) => {
|
||||
// console.log(e);
|
||||
// };
|
||||
|
||||
// return (
|
||||
// <div style={{ display: 'block' }}>
|
||||
// <Polar
|
||||
// data={{
|
||||
// labels: polarAreaChartLabels,
|
||||
// datasets: polarAreaChartData,
|
||||
// }}
|
||||
// type={polarAreaChartType}
|
||||
// onHover={chartHovered}
|
||||
// onClick={chartClicked}
|
||||
// />
|
||||
// </div>
|
||||
// );
|
||||
// };
|
||||
|
||||
// export default PolarChartComponent;
|
||||
|
||||
import React from 'react';
|
||||
import { PolarArea as Polar } from 'react-chartjs-2';
|
||||
import {
|
||||
Chart as ChartJS,
|
||||
RadialLinearScale,
|
||||
ArcElement,
|
||||
Tooltip,
|
||||
Legend,
|
||||
} from 'chart.js';
|
||||
|
||||
// Register required Chart.js components
|
||||
|
||||
|
||||
const PolarChartComponent = () => {
|
||||
// Chart data
|
||||
const data = {
|
||||
labels: [
|
||||
'Download Sales',
|
||||
'In-Store Sales',
|
||||
'Mail Sales',
|
||||
'Telesales',
|
||||
'Corporate Sales',
|
||||
],
|
||||
datasets: [
|
||||
{
|
||||
data: [300, 500, 100, 40, 120],
|
||||
backgroundColor: [
|
||||
'rgba(255, 99, 132, 0.6)',
|
||||
'rgba(54, 162, 235, 0.6)',
|
||||
'rgba(255, 206, 86, 0.6)',
|
||||
'rgba(75, 192, 192, 0.6)',
|
||||
'rgba(153, 102, 255, 0.6)',
|
||||
],
|
||||
borderColor: [
|
||||
'rgba(255, 99, 132, 1)',
|
||||
'rgba(54, 162, 235, 1)',
|
||||
'rgba(255, 206, 86, 1)',
|
||||
'rgba(75, 192, 192, 1)',
|
||||
'rgba(153, 102, 255, 1)',
|
||||
],
|
||||
borderWidth: 1,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// Chart options
|
||||
const options = {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'top',
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: (tooltipItem) => {
|
||||
return `${tooltipItem.label}: ${tooltipItem.raw}`;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
onClick: (event, elements) => {
|
||||
if (elements.length > 0) {
|
||||
const datasetIndex = elements[0].datasetIndex;
|
||||
const dataIndex = elements[0].index;
|
||||
console.log(
|
||||
`Clicked on dataset ${datasetIndex}, data index ${dataIndex}`
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ width: '50%', margin: 'auto' }}>
|
||||
<Polar data={data} options={options} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PolarChartComponent;
|
||||
@@ -0,0 +1,44 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Radar } from 'react-chartjs-2'; // Import Radar chart from react-chartjs-2
|
||||
import { Chart as ChartJS, RadarElement, Tooltip, Legend, PointElement, LineElement } from 'chart.js';
|
||||
|
||||
// ChartJS.register(RadarElement, Tooltip, Legend, PointElement, LineElement); // Register required chart elements for Radar chart
|
||||
|
||||
const RadarChartComponent = () => {
|
||||
// Define radar chart data, labels, and type
|
||||
const [radarChartLabels] = useState([
|
||||
'Eating', 'Drinking', 'Sleeping', 'Designing', 'Coding', 'Cycling', 'Running'
|
||||
]);
|
||||
|
||||
const [radarChartData] = useState([
|
||||
{ data: [65, 59, 90, 81, 56, 55, 40], label: 'Series A' },
|
||||
{ data: [28, 48, 40, 19, 96, 27, 100], label: 'Series B' }
|
||||
]);
|
||||
|
||||
const [radarChartType] = useState('radar');
|
||||
|
||||
// Handle chart hover and click events
|
||||
const chartClicked = (e) => {
|
||||
console.log(e);
|
||||
};
|
||||
|
||||
const chartHovered = (e) => {
|
||||
console.log(e);
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ display: 'block' }}>
|
||||
<Radar
|
||||
data={{
|
||||
labels: radarChartLabels,
|
||||
datasets: radarChartData,
|
||||
}}
|
||||
type={radarChartType}
|
||||
onHover={chartHovered}
|
||||
onClick={chartClicked}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RadarChartComponent;
|
||||
@@ -0,0 +1,55 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Scatter } from 'react-chartjs-2'; // Import Scatter chart from react-chartjs-2
|
||||
import { Chart as ChartJS, Tooltip, Legend, ScatterElement, PointElement } from 'chart.js';
|
||||
|
||||
// ChartJS.register(Tooltip, Legend, ScatterElement, PointElement); // Register required chart elements for scatter chart
|
||||
|
||||
const ScatterChartComponent = () => {
|
||||
// Define scatter chart data, labels, and type
|
||||
const [scatterChartLabels] = useState([
|
||||
'Eating', 'Drinking', 'Sleeping', 'Designing', 'Coding', 'Cycling', 'Running'
|
||||
]);
|
||||
|
||||
const [scatterChartData] = useState([
|
||||
{
|
||||
data: [
|
||||
{ x: 1, y: 1 },
|
||||
{ x: 2, y: 3 },
|
||||
{ x: 3, y: -2 },
|
||||
{ x: 4, y: 4 },
|
||||
{ x: 5, y: -3, r: 20 },
|
||||
],
|
||||
label: 'Series A',
|
||||
pointRadius: 10,
|
||||
backgroundColor: [
|
||||
'red', 'green', 'blue', 'purple', 'yellow', 'brown', 'magenta', 'cyan', 'orange', 'pink'
|
||||
]
|
||||
},
|
||||
]);
|
||||
|
||||
const [scatterChartType] = useState('scatter');
|
||||
|
||||
// Handle chart hover and click events
|
||||
const chartClicked = (e) => {
|
||||
console.log(e);
|
||||
};
|
||||
|
||||
const chartHovered = (e) => {
|
||||
console.log(e);
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ display: 'block' }}>
|
||||
<Scatter
|
||||
data={{
|
||||
datasets: scatterChartData,
|
||||
}}
|
||||
type={scatterChartType}
|
||||
onHover={chartHovered}
|
||||
onClick={chartClicked}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ScatterChartComponent;
|
||||
@@ -0,0 +1,70 @@
|
||||
import React, { useState } from 'react';
|
||||
|
||||
const ToDoChartComponent = () => {
|
||||
// State to store todo list and current todo input value
|
||||
const [todo, setTodo] = useState('');
|
||||
const [todoList, setTodoList] = useState(['todo 1']);
|
||||
|
||||
// Add a new todo to the list
|
||||
const addTodo = (newTodo) => {
|
||||
if (newTodo.trim()) {
|
||||
setTodoList([...todoList, newTodo]);
|
||||
setTodo(''); // Clear the input field after adding
|
||||
}
|
||||
};
|
||||
|
||||
// Remove a todo from the list by index
|
||||
const removeTodo = (index) => {
|
||||
const updatedTodoList = todoList.filter((_, i) => i !== index);
|
||||
setTodoList(updatedTodoList);
|
||||
};
|
||||
|
||||
return (
|
||||
<table className="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="c-col">#</th>
|
||||
<th>Item</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{todoList.map((todoItem, index) => (
|
||||
<tr className="ui basic segment" key={index}>
|
||||
<td className="c-col">{index + 1}</td>
|
||||
<td>{todoItem}</td>
|
||||
<td style={{ textAlign: 'right' }}>
|
||||
<button
|
||||
onClick={() => removeTodo(index)}
|
||||
style={{ background: 'none', border: 'none' }}
|
||||
>
|
||||
<i className="clr-icon times"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>
|
||||
<input
|
||||
value={todo}
|
||||
onChange={(e) => setTodo(e.target.value)}
|
||||
placeholder="Add Todo"
|
||||
className="clr-input"
|
||||
/>
|
||||
</td>
|
||||
<td style={{ textAlign: 'right' }}>
|
||||
<button
|
||||
onClick={() => addTodo(todo)}
|
||||
style={{ background: 'none', border: 'none' }}
|
||||
>
|
||||
<i className="clr-icon plus"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
};
|
||||
|
||||
export default ToDoChartComponent;
|
||||
11
src/components/Dashboard/dashboardnew/schedule/Schedule.js
Normal file
11
src/components/Dashboard/dashboardnew/schedule/Schedule.js
Normal file
@@ -0,0 +1,11 @@
|
||||
import React from 'react';
|
||||
|
||||
const ScheduleComponent = () => {
|
||||
return (
|
||||
<div>
|
||||
<p>Schedule works!</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ScheduleComponent;
|
||||
89
src/components/Footers/AdminFooter.js
Normal file
89
src/components/Footers/AdminFooter.js
Normal file
@@ -0,0 +1,89 @@
|
||||
/*!
|
||||
|
||||
=========================================================
|
||||
* Argon Dashboard React - v1.2.4
|
||||
=========================================================
|
||||
|
||||
* Product Page: https://www.creative-tim.com/product/argon-dashboard-react
|
||||
* Copyright 2024 Creative Tim (https://www.creative-tim.com)
|
||||
* Licensed under MIT (https://github.com/creativetimofficial/argon-dashboard-react/blob/master/LICENSE.md)
|
||||
|
||||
* Coded by Creative Tim
|
||||
|
||||
=========================================================
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
*/
|
||||
/*eslint-disable*/
|
||||
|
||||
// reactstrap components
|
||||
import { Container, Row, Col, Nav, NavItem, NavLink } from "reactstrap";
|
||||
|
||||
const Footer = () => {
|
||||
return (
|
||||
<footer className="footer">
|
||||
<Row className="align-items-center justify-content-xl-between">
|
||||
<Col xl="6">
|
||||
<div className="copyright text-center text-xl-left text-muted">
|
||||
© {new Date().getFullYear()}{" "}
|
||||
<a
|
||||
className="font-weight-bold ml-1"
|
||||
href="https://www.creative-tim.com?ref=adr-admin-footer"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
Creative Tim
|
||||
</a>
|
||||
</div>
|
||||
</Col>
|
||||
|
||||
<Col xl="6">
|
||||
<Nav className="nav-footer justify-content-center justify-content-xl-end">
|
||||
<NavItem>
|
||||
<NavLink
|
||||
href="https://www.creative-tim.com?ref=adr-admin-footer"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
Creative Tim
|
||||
</NavLink>
|
||||
</NavItem>
|
||||
|
||||
<NavItem>
|
||||
<NavLink
|
||||
href="https://www.creative-tim.com/presentation?ref=adr-admin-footer"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
About Us
|
||||
</NavLink>
|
||||
</NavItem>
|
||||
|
||||
<NavItem>
|
||||
<NavLink
|
||||
href="http://blog.creative-tim.com?ref=adr-admin-footer"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
Blog
|
||||
</NavLink>
|
||||
</NavItem>
|
||||
|
||||
<NavItem>
|
||||
<NavLink
|
||||
href="https://github.com/creativetimofficial/argon-dashboard/blob/master/LICENSE.md?ref=adr-admin-footer"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
MIT License
|
||||
</NavLink>
|
||||
</NavItem>
|
||||
</Nav>
|
||||
</Col>
|
||||
</Row>
|
||||
</footer>
|
||||
);
|
||||
};
|
||||
|
||||
export default Footer;
|
||||
84
src/components/Footers/AuthFooter.js
Normal file
84
src/components/Footers/AuthFooter.js
Normal file
@@ -0,0 +1,84 @@
|
||||
/*!
|
||||
|
||||
=========================================================
|
||||
* Argon Dashboard React - v1.2.4
|
||||
=========================================================
|
||||
|
||||
* Product Page: https://www.creative-tim.com/product/argon-dashboard-react
|
||||
* Copyright 2024 Creative Tim (https://www.creative-tim.com)
|
||||
* Licensed under MIT (https://github.com/creativetimofficial/argon-dashboard-react/blob/master/LICENSE.md)
|
||||
|
||||
* Coded by Creative Tim
|
||||
|
||||
=========================================================
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
*/
|
||||
/*eslint-disable*/
|
||||
|
||||
// reactstrap components
|
||||
import { NavItem, NavLink, Nav, Container, Row, Col } from "reactstrap";
|
||||
|
||||
const Login = () => {
|
||||
return (
|
||||
<>
|
||||
<footer className="py-5">
|
||||
<Container>
|
||||
<Row className="align-items-center justify-content-xl-between">
|
||||
<Col xl="6">
|
||||
<div className="copyright text-center text-xl-left text-muted">
|
||||
© {new Date().getFullYear()}{" "}
|
||||
<a
|
||||
className="font-weight-bold ml-1"
|
||||
href="https://www.creative-tim.com?ref=adr-auth-footer"
|
||||
target="_blank"
|
||||
>
|
||||
Creative Tim
|
||||
</a>
|
||||
</div>
|
||||
</Col>
|
||||
<Col xl="6">
|
||||
<Nav className="nav-footer justify-content-center justify-content-xl-end">
|
||||
<NavItem>
|
||||
<NavLink
|
||||
href="https://www.creative-tim.com?ref=adr-auth-footer"
|
||||
target="_blank"
|
||||
>
|
||||
Creative Tim
|
||||
</NavLink>
|
||||
</NavItem>
|
||||
<NavItem>
|
||||
<NavLink
|
||||
href="https://www.creative-tim.com/presentation?ref=adr-auth-footer"
|
||||
target="_blank"
|
||||
>
|
||||
About Us
|
||||
</NavLink>
|
||||
</NavItem>
|
||||
<NavItem>
|
||||
<NavLink
|
||||
href="http://blog.creative-tim.com?ref=adr-auth-footer"
|
||||
target="_blank"
|
||||
>
|
||||
Blog
|
||||
</NavLink>
|
||||
</NavItem>
|
||||
<NavItem>
|
||||
<NavLink
|
||||
href="https://github.com/creativetimofficial/argon-dashboard/blob/master/LICENSE.md?ref=adr-auth-footer"
|
||||
target="_blank"
|
||||
>
|
||||
MIT License
|
||||
</NavLink>
|
||||
</NavItem>
|
||||
</Nav>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
</footer>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Login;
|
||||
291
src/components/Headers/Header.js
Normal file
291
src/components/Headers/Header.js
Normal file
@@ -0,0 +1,291 @@
|
||||
/*!
|
||||
|
||||
=========================================================
|
||||
* Argon Dashboard React - v1.2.4
|
||||
=========================================================
|
||||
|
||||
* Product Page: https://www.creative-tim.com/product/argon-dashboard-react
|
||||
* Copyright 2024 Creative Tim (https://www.creative-tim.com)
|
||||
* Licensed under MIT (https://github.com/creativetimofficial/argon-dashboard-react/blob/master/LICENSE.md)
|
||||
|
||||
* Coded by Creative Tim
|
||||
|
||||
=========================================================
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
*/
|
||||
|
||||
// reactstrap components
|
||||
// import { Card, CardBody, CardTitle, Container, Row, Col } from "reactstrap";
|
||||
|
||||
// const Header = () => {
|
||||
// return (
|
||||
// <>
|
||||
// <div className="header bg-gradient-info pb-8 pt-5 pt-md-10">
|
||||
// <Container fluid>
|
||||
// <div className="header-body">
|
||||
// {/* Card stats */}
|
||||
// <Row >
|
||||
// <Col lg="6" xl="3" className="d-flex align-items-stretch">
|
||||
// <Card className="card-stats mb-4 mb-xl-0 w-100">
|
||||
// <CardBody>
|
||||
// <Row>
|
||||
// <div className="col">
|
||||
// <CardTitle
|
||||
// tag="h5"
|
||||
// className="text-uppercase text-muted mb-0"
|
||||
// >
|
||||
// Traffic
|
||||
// </CardTitle>
|
||||
// <span className="h2 font-weight-bold mb-0">
|
||||
// 350,897
|
||||
// </span>
|
||||
// </div>
|
||||
// <Col className="col-auto">
|
||||
// <div className="icon icon-shape bg-danger text-white rounded-circle shadow">
|
||||
// <i className="fas fa-chart-bar" />
|
||||
// </div>
|
||||
// </Col>
|
||||
// </Row>
|
||||
// <p className="mt-3 mb-0 text-muted text-sm">
|
||||
// <span className="text-success mr-2">
|
||||
// <i className="fa fa-arrow-up" /> 3.48%
|
||||
// </span>{" "}
|
||||
// <span className="text-nowrap">Since last month</span>
|
||||
// </p>
|
||||
// </CardBody>
|
||||
// </Card>
|
||||
// </Col>
|
||||
// <Col lg="6" xl="3">
|
||||
// <Card className="card-stats mb-4 mb-xl-0">
|
||||
// <CardBody>
|
||||
// <Row>
|
||||
// <div className="col">
|
||||
// <CardTitle
|
||||
// tag="h5"
|
||||
// className="text-uppercase text-muted mb-0"
|
||||
// >
|
||||
// New users
|
||||
// </CardTitle>
|
||||
// <span className="h2 font-weight-bold mb-0">2,356</span>
|
||||
// </div>
|
||||
// <Col className="col-auto">
|
||||
// <div className="icon icon-shape bg-warning text-white rounded-circle shadow">
|
||||
// <i className="fas fa-chart-pie" />
|
||||
// </div>
|
||||
// </Col>
|
||||
// </Row>
|
||||
// <p className="mt-3 mb-0 text-muted text-sm">
|
||||
// <span className="text-danger mr-2">
|
||||
// <i className="fas fa-arrow-down" /> 3.48%
|
||||
// </span>{" "}
|
||||
// <span className="text-nowrap">Since last week</span>
|
||||
// </p>
|
||||
// </CardBody>
|
||||
// </Card>
|
||||
// </Col>
|
||||
// <Col lg="6" xl="3">
|
||||
// <Card className="card-stats mb-4 mb-xl-0">
|
||||
// <CardBody>
|
||||
// <Row>
|
||||
// <div className="col">
|
||||
// <CardTitle
|
||||
// tag="h5"
|
||||
// className="text-uppercase text-muted mb-0"
|
||||
// >
|
||||
// Sales
|
||||
// </CardTitle>
|
||||
// <span className="h2 font-weight-bold mb-0">924</span>
|
||||
// </div>
|
||||
// <Col className="col-auto">
|
||||
// <div className="icon icon-shape bg-yellow text-white rounded-circle shadow">
|
||||
// <i className="fas fa-users" />
|
||||
// </div>
|
||||
// </Col>
|
||||
// </Row>
|
||||
// <p className="mt-3 mb-0 text-muted text-sm">
|
||||
// <span className="text-warning mr-2">
|
||||
// <i className="fas fa-arrow-down" /> 1.10%
|
||||
// </span>{" "}
|
||||
// <span className="text-nowrap">Since yesterday</span>
|
||||
// </p>
|
||||
// </CardBody>
|
||||
// </Card>
|
||||
// </Col>
|
||||
// <Col lg="6" xl="3">
|
||||
// <Card className="card-stats mb-4 mb-xl-0">
|
||||
// <CardBody>
|
||||
// <Row>
|
||||
// <div className="col">
|
||||
// <CardTitle
|
||||
// tag="h5"
|
||||
// className="text-uppercase text-muted mb-0"
|
||||
// >
|
||||
// Performance
|
||||
// </CardTitle>
|
||||
// <span className="h2 font-weight-bold mb-0">49,65%</span>
|
||||
// </div>
|
||||
// <Col className="col-auto">
|
||||
// <div className="icon icon-shape bg-info text-white rounded-circle shadow">
|
||||
// <i className="fas fa-percent" />
|
||||
// </div>
|
||||
// </Col>
|
||||
// </Row>
|
||||
// <p className="mt-3 mb-0 text-muted text-sm">
|
||||
// <span className="text-success mr-2">
|
||||
// <i className="fas fa-arrow-up" /> 12%
|
||||
// </span>{" "}
|
||||
// <span className="text-nowrap">Since last month</span>
|
||||
// </p>
|
||||
// </CardBody>
|
||||
// </Card>
|
||||
// </Col>
|
||||
// </Row>
|
||||
// </div>
|
||||
// </Container>
|
||||
// </div>
|
||||
// </>
|
||||
// );
|
||||
// };
|
||||
|
||||
// export default Header;
|
||||
|
||||
import { Card, CardBody, CardTitle, Container, Row, Col } from "reactstrap";
|
||||
|
||||
const Header = () => {
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="header bg-gradient-info pb-8 pt-7 pt-md-12"
|
||||
style={{ marginTop: "6rem" }}
|
||||
>
|
||||
{" "}
|
||||
{/* Adjust pt-md-10 to add more space from top */}
|
||||
<Container fluid>
|
||||
<div className="header-body">
|
||||
{/* Card stats */}
|
||||
<Row className="d-flex">
|
||||
<Col lg="6" xl="3" className="d-flex align-items-stretch">
|
||||
<Card className="card-stats mb-4 mb-xl-0 w-100">
|
||||
<CardBody>
|
||||
<Row>
|
||||
<div className="col">
|
||||
<CardTitle
|
||||
tag="h5"
|
||||
className="text-uppercase text-muted mb-0"
|
||||
>
|
||||
Traffic
|
||||
</CardTitle>
|
||||
<span className="h2 font-weight-bold mb-0">
|
||||
350,897
|
||||
</span>
|
||||
</div>
|
||||
<Col className="col-auto">
|
||||
<div className="icon icon-shape bg-danger text-white rounded-circle shadow">
|
||||
<i className="fas fa-chart-bar" />
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<p className="mt-3 mb-0 text-muted text-sm">
|
||||
<span className="text-success mr-2">
|
||||
<i className="fa fa-arrow-up" /> 3.48%
|
||||
</span>{" "}
|
||||
<span className="text-nowrap">Since last month</span>
|
||||
</p>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col lg="6" xl="3" className="d-flex align-items-stretch">
|
||||
<Card className="card-stats mb-4 mb-xl-0 w-100">
|
||||
<CardBody>
|
||||
<Row>
|
||||
<div className="col">
|
||||
<CardTitle
|
||||
tag="h5"
|
||||
className="text-uppercase text-muted mb-0"
|
||||
>
|
||||
New users
|
||||
</CardTitle>
|
||||
<span className="h2 font-weight-bold mb-0">2,356</span>
|
||||
</div>
|
||||
<Col className="col-auto">
|
||||
<div className="icon icon-shape bg-warning text-white rounded-circle shadow">
|
||||
<i className="fas fa-chart-pie" />
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<p className="mt-3 mb-0 text-muted text-sm">
|
||||
<span className="text-danger mr-2">
|
||||
<i className="fas fa-arrow-down" /> 3.48%
|
||||
</span>{" "}
|
||||
<span className="text-nowrap">Since last week</span>
|
||||
</p>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col lg="6" xl="3" className="d-flex align-items-stretch">
|
||||
<Card className="card-stats mb-4 mb-xl-0 w-100">
|
||||
<CardBody>
|
||||
<Row>
|
||||
<div className="col">
|
||||
<CardTitle
|
||||
tag="h5"
|
||||
className="text-uppercase text-muted mb-0"
|
||||
>
|
||||
Sales
|
||||
</CardTitle>
|
||||
<span className="h2 font-weight-bold mb-0">924</span>
|
||||
</div>
|
||||
<Col className="col-auto">
|
||||
<div className="icon icon-shape bg-yellow text-white rounded-circle shadow">
|
||||
<i className="fas fa-users" />
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<p className="mt-3 mb-0 text-muted text-sm">
|
||||
<span className="text-warning mr-2">
|
||||
<i className="fas fa-arrow-down" /> 1.10%
|
||||
</span>{" "}
|
||||
<span className="text-nowrap">Since yesterday</span>
|
||||
</p>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col lg="6" xl="3" className="d-flex align-items-stretch">
|
||||
<Card className="card-stats mb-4 mb-xl-0 w-100">
|
||||
<CardBody>
|
||||
<Row>
|
||||
<div className="col">
|
||||
<CardTitle
|
||||
tag="h5"
|
||||
className="text-uppercase text-muted mb-0"
|
||||
>
|
||||
Performance
|
||||
</CardTitle>
|
||||
<span className="h2 font-weight-bold mb-0">49,65%</span>
|
||||
</div>
|
||||
<Col className="col-auto">
|
||||
<div className="icon icon-shape bg-info text-white rounded-circle shadow">
|
||||
<i className="fas fa-percent" />
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<p className="mt-3 mb-0 text-muted text-sm">
|
||||
<span className="text-success mr-2">
|
||||
<i className="fas fa-arrow-up" /> 12%
|
||||
</span>{" "}
|
||||
<span className="text-nowrap">Since last month</span>
|
||||
</p>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
</Container>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Header;
|
||||
61
src/components/Headers/UserHeader.js
Normal file
61
src/components/Headers/UserHeader.js
Normal file
@@ -0,0 +1,61 @@
|
||||
/*!
|
||||
|
||||
=========================================================
|
||||
* Argon Dashboard React - v1.2.4
|
||||
=========================================================
|
||||
|
||||
* Product Page: https://www.creative-tim.com/product/argon-dashboard-react
|
||||
* Copyright 2024 Creative Tim (https://www.creative-tim.com)
|
||||
* Licensed under MIT (https://github.com/creativetimofficial/argon-dashboard-react/blob/master/LICENSE.md)
|
||||
|
||||
* Coded by Creative Tim
|
||||
|
||||
=========================================================
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
*/
|
||||
|
||||
// reactstrap components
|
||||
import { Button, Container, Row, Col } from "reactstrap";
|
||||
|
||||
const UserHeader = () => {
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="header pb-8 pt-5 pt-lg-8 d-flex align-items-center"
|
||||
style={{
|
||||
minHeight: "600px",
|
||||
backgroundImage:
|
||||
"url(" + require("../../assets/img/theme/profile-cover.jpg") + ")",
|
||||
backgroundSize: "cover",
|
||||
backgroundPosition: "center top",
|
||||
}}
|
||||
>
|
||||
{/* Mask */}
|
||||
<span className="mask bg-gradient-default opacity-8" />
|
||||
{/* Header container */}
|
||||
<Container className="d-flex align-items-center" fluid>
|
||||
<Row>
|
||||
<Col lg="7" md="10">
|
||||
<h1 className="display-2 text-white">Hello Jesse</h1>
|
||||
<p className="text-white mt-0 mb-5">
|
||||
This is your profile page. You can see the progress you've made
|
||||
with your work and manage your projects or assigned tasks
|
||||
</p>
|
||||
<Button
|
||||
color="info"
|
||||
href="#pablo"
|
||||
onClick={(e) => e.preventDefault()}
|
||||
>
|
||||
Edit profile
|
||||
</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserHeader;
|
||||
173
src/components/Navbars/AdminNavbar.css
Normal file
173
src/components/Navbars/AdminNavbar.css
Normal file
@@ -0,0 +1,173 @@
|
||||
/* Navbar */
|
||||
#navbar-main {
|
||||
background: linear-gradient(90deg, #0e6591 0%, #1a8bc5 100%);
|
||||
padding: 0.5rem 1rem;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1030;
|
||||
}
|
||||
|
||||
/* Container */
|
||||
.navbar-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 2rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Brand */
|
||||
.brand_name {
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
font-size: 1.6rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
transition: color 0.3s ease, transform 0.3s ease;
|
||||
}
|
||||
|
||||
.brand_name:hover {
|
||||
color: #ffd700;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
/* Navigation Icons */
|
||||
.nav-icons {
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.nav-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
transition: color 0.3s ease, transform 0.3s ease;
|
||||
}
|
||||
|
||||
.nav-icon i {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.nav-icon:hover {
|
||||
color: #ffd700;
|
||||
transform: scale(1.15);
|
||||
}
|
||||
|
||||
/* Search Bar */
|
||||
.search-input-group {
|
||||
border-radius: 30px;
|
||||
background-color: #fff;
|
||||
border: none;
|
||||
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.1);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.search-input-group:hover,
|
||||
.search-input-group:focus-within {
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.15);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
background-color: #0078b9;
|
||||
color: #fff;
|
||||
border-radius: 50%;
|
||||
padding: 0.5rem;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
.search-icon:hover {
|
||||
background-color: #005579;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
border: none;
|
||||
border-radius: 30px;
|
||||
padding: 0.75rem 1rem;
|
||||
font-size: 0.95rem;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.search-input:focus {
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* Profile Dropdown */
|
||||
.nav-right {
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
border: 3px solid #fff;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.avatar:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.15);
|
||||
animation: slideDown 0.2s ease-out;
|
||||
}
|
||||
|
||||
@keyframes slideDown {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
font-size: 0.9rem;
|
||||
padding: 0.5rem 1rem;
|
||||
color: #333;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.dropdown-item:hover {
|
||||
background-color: #f1f5f9;
|
||||
color: #0078b9;
|
||||
}
|
||||
|
||||
.dropdown-item:last-child {
|
||||
color: #dc3545;
|
||||
}
|
||||
|
||||
.dropdown-item:last-child:hover {
|
||||
background-color: #fef2f2;
|
||||
}
|
||||
|
||||
/* Body Adjustment */
|
||||
body {
|
||||
padding-top: 60px;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 768px) {
|
||||
.navbar-container {
|
||||
flex-direction: column;
|
||||
padding: 0.5rem;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.nav-icons {
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.search-input-group {
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
}
|
||||
}
|
||||
121
src/components/Navbars/AdminNavbar.js
Normal file
121
src/components/Navbars/AdminNavbar.js
Normal file
@@ -0,0 +1,121 @@
|
||||
import { useNavigate, Link } from "react-router-dom";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownItem,
|
||||
UncontrolledDropdown,
|
||||
DropdownToggle,
|
||||
Form,
|
||||
FormGroup,
|
||||
InputGroupAddon,
|
||||
InputGroupText,
|
||||
Input,
|
||||
InputGroup,
|
||||
Navbar,
|
||||
Nav,
|
||||
Media,
|
||||
} from "reactstrap";
|
||||
import "./AdminNavbar.css";
|
||||
import "bootstrap-icons/font/bootstrap-icons.css";
|
||||
|
||||
const AdminNavbar = ({ brandText }) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleLogout = () => {
|
||||
// localStorage.removeItem("authToken"); // Uncomment if needed
|
||||
navigate("/login");
|
||||
};
|
||||
|
||||
return (
|
||||
<Navbar className="navbar-top navbar-dark" expand="lg" id="navbar-main">
|
||||
<div className="navbar-container container-fluid">
|
||||
{/* Brand */}
|
||||
<Link className="brand_name h4 mb-0 text-white text-uppercase" to="/">
|
||||
{brandText}
|
||||
</Link>
|
||||
|
||||
{/* Navigation Icons */}
|
||||
<div className="nav-icons d-flex align-items-center">
|
||||
<Link to="/admin/index" className="nav-icon">
|
||||
<i className="fas fa-home" />
|
||||
</Link>
|
||||
<Link to="/admin/setting" className="nav-icon">
|
||||
<i className="ni ni-settings-gear-65" />
|
||||
</Link>
|
||||
<Link to="/admin/dashboard-runner-all" className="nav-icon">
|
||||
<i className="bi bi-grid" />
|
||||
</Link>
|
||||
<Link to="/admin/report-runner" className="nav-icon">
|
||||
<i className="ni ni-chart-bar-32" />
|
||||
</Link>
|
||||
<Link to="/admin/user-report" className="nav-icon">
|
||||
<i className="ni ni-grid" />
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Search and Profile */}
|
||||
<div className="nav-right d-flex align-items-center">
|
||||
<Form className="navbar-search navbar-search-dark form-inline">
|
||||
<FormGroup className="mb-0">
|
||||
<InputGroup className="input-group-alternative search-input-group">
|
||||
<InputGroupAddon addonType="prepend">
|
||||
<InputGroupText className="search-icon">
|
||||
<i className="fas fa-search" />
|
||||
</InputGroupText>
|
||||
</InputGroupAddon>
|
||||
<Input placeholder="Search" type="text" className="search-input" />
|
||||
</InputGroup>
|
||||
</FormGroup>
|
||||
</Form>
|
||||
|
||||
<Nav className="align-items-center" navbar>
|
||||
<UncontrolledDropdown nav>
|
||||
<DropdownToggle className="pr-0" nav>
|
||||
<Media className="align-items-center">
|
||||
<span className="avatar avatar-sm rounded-circle">
|
||||
<img
|
||||
alt="Profile"
|
||||
src={require("../../assets/img/theme/team-4-800x800.jpg")}
|
||||
/>
|
||||
</span>
|
||||
<Media className="ml-2 d-none d-lg-block">
|
||||
<span className="brand_name mb-0 text-sm font-weight-bold text-white-50">
|
||||
Jessica Jones
|
||||
</span>
|
||||
</Media>
|
||||
</Media>
|
||||
</DropdownToggle>
|
||||
<DropdownMenu className="dropdown-menu-arrow" right>
|
||||
<DropdownItem className="noti-title" header tag="div">
|
||||
<h6 className="text-overflow m-0">Welcome!</h6>
|
||||
</DropdownItem>
|
||||
<DropdownItem to="/admin/user-profile" tag={Link}>
|
||||
<i className="ni ni-single-02" />
|
||||
<span>My profile</span>
|
||||
</DropdownItem>
|
||||
<DropdownItem to="/admin/user-profile" tag={Link}>
|
||||
<i className="ni ni-settings-gear-65" />
|
||||
<span>Settings</span>
|
||||
</DropdownItem>
|
||||
<DropdownItem to="/admin/user-profile" tag={Link}>
|
||||
<i className="ni ni-calendar-grid-58" />
|
||||
<span>Activity</span>
|
||||
</DropdownItem>
|
||||
<DropdownItem to="/admin/user-profile" tag={Link}>
|
||||
<i className="ni ni-support-16" />
|
||||
<span>Support</span>
|
||||
</DropdownItem>
|
||||
<DropdownItem divider />
|
||||
<DropdownItem onClick={handleLogout}>
|
||||
<i className="ni ni-user-run" />
|
||||
<span>Logout</span>
|
||||
</DropdownItem>
|
||||
</DropdownMenu>
|
||||
</UncontrolledDropdown>
|
||||
</Nav>
|
||||
</div>
|
||||
</div>
|
||||
</Navbar>
|
||||
);
|
||||
};
|
||||
|
||||
export default AdminNavbar;
|
||||
90
src/components/Navbars/AuthNavbar.js
Normal file
90
src/components/Navbars/AuthNavbar.js
Normal file
@@ -0,0 +1,90 @@
|
||||
|
||||
import { Link } from "react-router-dom";
|
||||
// reactstrap components
|
||||
import {
|
||||
UncontrolledCollapse,
|
||||
NavbarBrand,
|
||||
Navbar,
|
||||
NavItem,
|
||||
NavLink,
|
||||
Nav,
|
||||
Container,
|
||||
Row,
|
||||
Col,
|
||||
} from "reactstrap";
|
||||
|
||||
const AdminNavbar = () => {
|
||||
return (
|
||||
<>
|
||||
<Navbar className="navbar-top navbar-horizontal navbar-dark" expand="md">
|
||||
<Container className="px-4">
|
||||
<NavbarBrand to="/" tag={Link}>
|
||||
<img
|
||||
alt="..."
|
||||
src={require("../../assets/img/brand/argon-react-white.png")}
|
||||
/>
|
||||
</NavbarBrand>
|
||||
<button className="navbar-toggler" id="navbar-collapse-main">
|
||||
<span className="navbar-toggler-icon" />
|
||||
</button>
|
||||
<UncontrolledCollapse navbar toggler="#navbar-collapse-main">
|
||||
<div className="navbar-collapse-header d-md-none">
|
||||
<Row>
|
||||
<Col className="collapse-brand" xs="6">
|
||||
<Link to="/">
|
||||
<img
|
||||
alt="..."
|
||||
src={require("../../assets/img/brand/argon-react.png")}
|
||||
/>
|
||||
</Link>
|
||||
</Col>
|
||||
<Col className="collapse-close" xs="6">
|
||||
<button className="navbar-toggler" id="navbar-collapse-main">
|
||||
<span />
|
||||
<span />
|
||||
</button>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
<Nav className="ml-auto" navbar>
|
||||
<NavItem>
|
||||
<NavLink className="nav-link-icon" to="/" tag={Link}>
|
||||
<i className="ni ni-planet" />
|
||||
<span className="nav-link-inner--text">Dashboard</span>
|
||||
</NavLink>
|
||||
</NavItem>
|
||||
<NavItem>
|
||||
<NavLink
|
||||
className="nav-link-icon"
|
||||
to="/auth/register"
|
||||
tag={Link}
|
||||
>
|
||||
<i className="ni ni-circle-08" />
|
||||
<span className="nav-link-inner--text">Register</span>
|
||||
</NavLink>
|
||||
</NavItem>
|
||||
<NavItem>
|
||||
<NavLink className="nav-link-icon" to="/auth/login" tag={Link}>
|
||||
<i className="ni ni-key-25" />
|
||||
<span className="nav-link-inner--text">Login</span>
|
||||
</NavLink>
|
||||
</NavItem>
|
||||
<NavItem>
|
||||
<NavLink
|
||||
className="nav-link-icon"
|
||||
to="/admin/user-profile"
|
||||
tag={Link}
|
||||
>
|
||||
<i className="ni ni-single-02" />
|
||||
<span className="nav-link-inner--text">Profile</span>
|
||||
</NavLink>
|
||||
</NavItem>
|
||||
</Nav>
|
||||
</UncontrolledCollapse>
|
||||
</Container>
|
||||
</Navbar>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default AdminNavbar;
|
||||
86
src/components/Sidebar/Sidebar.css
Normal file
86
src/components/Sidebar/Sidebar.css
Normal file
@@ -0,0 +1,86 @@
|
||||
.sidebar-wrapper {
|
||||
height: calc(100vh - 70px);
|
||||
position: fixed;
|
||||
top: 70px;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.app-sidebar {
|
||||
border-right: 1px solid #e9ecef;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
padding: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.logo-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
/*
|
||||
.navbar-brand-img {
|
||||
height: 40px;
|
||||
width: auto;
|
||||
} */
|
||||
|
||||
.sidebar-toggle-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: #5e72e4;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding: 0.5rem;
|
||||
margin-left: -1rem;
|
||||
transition: margin-left 0.3s ease;
|
||||
}
|
||||
|
||||
/* .sidebar-collapsed .sidebar-toggle-btn {
|
||||
margin-left: -1rem;
|
||||
} */
|
||||
|
||||
.sidebar-toggle-btn:hover {
|
||||
color: #233dd2;
|
||||
}
|
||||
|
||||
/* Pro Sidebar Custom Styles */
|
||||
.ps-menu-button {
|
||||
padding: 0.75rem 2rem !important;
|
||||
}
|
||||
|
||||
.ps-menu-button:hover {
|
||||
background-color: #f6f9fc !important;
|
||||
}
|
||||
|
||||
.ps-menu-button.ps-active {
|
||||
background-color: #5e72e4 !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.ps-submenu-content {
|
||||
background-color: #f8f9fe !important;
|
||||
}
|
||||
|
||||
.ps-menu-icon {
|
||||
font-size: 1.1rem !important;
|
||||
width: 35px !important;
|
||||
min-width: 35px !important;
|
||||
margin-right: 0.5rem !important;
|
||||
}
|
||||
|
||||
/* Adjust main content margin when sidebar is expanded/collapsed */
|
||||
.main-content {
|
||||
margin-left: 250px;
|
||||
transition: margin-left 0.3s ease;
|
||||
}
|
||||
|
||||
.main-content.sidebar-collapsed-main {
|
||||
margin-left: 50px;
|
||||
}
|
||||
140
src/components/Sidebar/Sidebar.js
Normal file
140
src/components/Sidebar/Sidebar.js
Normal file
@@ -0,0 +1,140 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { Link, useLocation } from "react-router-dom";
|
||||
import PropTypes from "prop-types";
|
||||
import { Sidebar as ProSidebar, Menu, MenuItem, SubMenu } from "react-pro-sidebar";
|
||||
import { fetchMenuItems } from "../../APIServices/MenuAPI";
|
||||
import routes from "routes.js";
|
||||
import "../Sidebar/Sidebar.css";
|
||||
|
||||
const Sidebar = (props) => {
|
||||
const [menuItems, setMenuItems] = useState([]);
|
||||
const [collapsed, setCollapsed] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
const location = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
loadMenuItems();
|
||||
}, []);
|
||||
|
||||
const loadMenuItems = async () => {
|
||||
try {
|
||||
const respdata = await fetchMenuItems();
|
||||
const data = respdata.data;
|
||||
if (Array.isArray(data)) {
|
||||
const dynamicRoutes = mapMenuToRoutes(data);
|
||||
setMenuItems(dynamicRoutes);
|
||||
} else if (data && typeof data === "object") {
|
||||
const dynamicRoutes = mapMenuToRoutes([data]);
|
||||
setMenuItems(dynamicRoutes);
|
||||
} else {
|
||||
console.error("Invalid menu data format:", data);
|
||||
setError("Invalid menu data format.");
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Error fetching menu items:", err);
|
||||
setError(err.message || "Failed to fetch menu items.");
|
||||
}
|
||||
};
|
||||
|
||||
const mapMenuToRoutes = (menuArray) => {
|
||||
if (!Array.isArray(menuArray)) {
|
||||
console.error('Expected an array for menuArray, received:', menuArray);
|
||||
return [];
|
||||
}
|
||||
|
||||
return menuArray.map(menu => ({
|
||||
path: menu.path || '#',
|
||||
name: menu.name || menu.menuName || '',
|
||||
icon: menu.icon || 'bi bi-grid',
|
||||
submenu: Array.isArray(menu.submenu) ? mapMenuToRoutes(menu.submenu) : []
|
||||
}));
|
||||
};
|
||||
|
||||
const toggleSidebar = () => {
|
||||
setCollapsed(!collapsed);
|
||||
if (props.onSidebarToggle) {
|
||||
props.onSidebarToggle(!collapsed);
|
||||
}
|
||||
};
|
||||
|
||||
const renderMenuItems = () => {
|
||||
const allRoutes = [...routes, ...menuItems];
|
||||
|
||||
return allRoutes.map((route, key) => {
|
||||
if (route.submenu && route.submenu.length > 0) {
|
||||
return (
|
||||
<SubMenu
|
||||
key={key}
|
||||
label={route.name}
|
||||
icon={<i className={route.icon} />}
|
||||
>
|
||||
{route.submenu.map((submenu, subKey) => (
|
||||
<MenuItem
|
||||
key={`${key}-${subKey}`}
|
||||
component={<Link to={submenu.path} />}
|
||||
icon={<i className={submenu.icon || "bi bi-grid"} />}
|
||||
>
|
||||
{submenu.name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</SubMenu>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<MenuItem
|
||||
key={key}
|
||||
component={<Link to={route.path} />}
|
||||
icon={<i className={route.icon || "bi bi-grid"} />}
|
||||
>
|
||||
{route.name}
|
||||
</MenuItem>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const { logo } = props;
|
||||
|
||||
return (
|
||||
<div className="sidebar-wrapper">
|
||||
<ProSidebar
|
||||
collapsed={collapsed}
|
||||
className="app-sidebar"
|
||||
backgroundColor="white"
|
||||
width="220px"
|
||||
collapsedWidth="30px"
|
||||
>
|
||||
<div className="sidebar-header">
|
||||
{/* {logo && (
|
||||
<Link to={logo.innerLink} className="logo-container">
|
||||
<img alt={logo.imgAlt} className="navbar-brand-img" src={logo.imgSrc} />
|
||||
</Link>
|
||||
)} */}
|
||||
<button
|
||||
className="sidebar-toggle-btn"
|
||||
type="button"
|
||||
onClick={toggleSidebar}
|
||||
title={collapsed ? "Expand sidebar" : "Collapse sidebar"}
|
||||
>
|
||||
<i className={`bi ${collapsed ? "bi-chevron-right" : "bi-chevron-left"}`} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<Menu>
|
||||
{renderMenuItems()}
|
||||
</Menu>
|
||||
</ProSidebar>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Sidebar.defaultProps = {
|
||||
logo: {},
|
||||
};
|
||||
|
||||
Sidebar.propTypes = {
|
||||
logo: PropTypes.object,
|
||||
onSidebarToggle: PropTypes.func,
|
||||
};
|
||||
|
||||
export default Sidebar;
|
||||
74
src/components/common/ConfirmModal.js
Normal file
74
src/components/common/ConfirmModal.js
Normal file
@@ -0,0 +1,74 @@
|
||||
import React from 'react';
|
||||
import { Modal, Button } from 'react-bootstrap';
|
||||
import { FaTimes } from 'react-icons/fa';
|
||||
|
||||
/**
|
||||
* A reusable confirmation modal component
|
||||
* @param {Object} props - Component props
|
||||
* @param {boolean} props.show - Controls modal visibility
|
||||
* @param {Function} props.onHide - Function to call when modal is closed
|
||||
* @param {Function} props.onConfirm - Function to call when action is confirmed
|
||||
* @param {string} props.title - Modal title
|
||||
* @param {string} props.message - Confirmation message
|
||||
* @param {string} props.confirmLabel - Label for the confirm button
|
||||
* @param {string} props.cancelLabel - Label for the cancel button
|
||||
* @param {string} props.variant - Bootstrap color variant for the confirm button
|
||||
*/
|
||||
const ConfirmModal = ({
|
||||
show,
|
||||
onHide,
|
||||
onConfirm,
|
||||
title = "Confirm Action",
|
||||
message = "Are you sure you want to proceed?",
|
||||
confirmLabel = "Confirm",
|
||||
cancelLabel = "Cancel",
|
||||
variant = "danger"
|
||||
}) => {
|
||||
return (
|
||||
<Modal show={show} onHide={onHide} centered>
|
||||
<Modal.Header>
|
||||
<Modal.Title style={{ color: "#0E6591" }}>
|
||||
{title}
|
||||
</Modal.Title>
|
||||
<div
|
||||
onClick={onHide}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
color: "#0E6591",
|
||||
fontSize: "1.5rem",
|
||||
position: "absolute",
|
||||
right: "15px",
|
||||
top: "15px",
|
||||
}}
|
||||
aria-label="Close"
|
||||
>
|
||||
<FaTimes />
|
||||
</div>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
<p>{message}</p>
|
||||
</Modal.Body>
|
||||
<Modal.Footer className="justify-content-between">
|
||||
<Button
|
||||
variant="outline-secondary"
|
||||
onClick={onHide}
|
||||
className="px-4"
|
||||
>
|
||||
{cancelLabel}
|
||||
</Button>
|
||||
<Button
|
||||
variant={variant}
|
||||
onClick={() => {
|
||||
onConfirm();
|
||||
onHide();
|
||||
}}
|
||||
className="px-4"
|
||||
>
|
||||
{confirmLabel}
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConfirmModal;
|
||||
Reference in New Issue
Block a user