This commit is contained in:
string 2025-06-04 15:09:28 +05:30
parent 509d01a4e7
commit 703f1256ff
49 changed files with 9665 additions and 7387 deletions

7619
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -2,11 +2,15 @@
"name": "log", "name": "log",
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"dependencies": { "dependencies": {
"@emotion/react": "^11.11.3", "@emotion/react": "^11.11.3",
"@emotion/styled": "^11.11.0", "@emotion/styled": "^11.11.0",
"@fortawesome/free-solid-svg-icons": "^6.5.2",
"@fortawesome/react-fontawesome": "^0.2.2",
"@mui/icons-material": "^5.15.20", "@mui/icons-material": "^5.15.20",
"@mui/material": "^5.15.20", "@mui/material": "^5.15.20",
"@mui/styles": "^5.16.4", "@mui/styles": "^5.16.4",
@ -17,23 +21,19 @@
"@testing-library/react": "^13.4.0", "@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0", "@testing-library/user-event": "^13.5.0",
"axios": "^1.6.7", "axios": "^1.6.7",
"lucide-react": "^0.511.0",
"mdb-react-ui-kit": "^7.1.0", "mdb-react-ui-kit": "^7.1.0",
"react": "^18.2.0", "react": "^18.2.0",
"react-barcode": "^1.5.3", "react-barcode": "^1.5.3",
"react-data-grid": "^7.0.0-beta.44", "react-data-grid": "^7.0.0-beta.44",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-google-recaptcha": "^3.1.0", "react-google-recaptcha": "^3.1.0",
"react-icons": "^5.2.1",
"react-qr-code": "^2.0.14", "react-qr-code": "^2.0.14",
"react-router-dom": "^6.21.3", "react-router-dom": "^6.21.3",
"react-scripts": "^5.0.1", "react-scripts": "^5.0.1",
"web-vitals": "^2.1.4" "web-vitals": "^2.1.4"
}, },
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": { "eslintConfig": {
"extends": [ "extends": [
"react-app", "react-app",

View File

@ -46,3 +46,4 @@
/* .login-container input[type="checkbox"] { /* .login-container input[type="checkbox"] {
margin-right: 5px; margin-right: 5px;
} */ } */

View File

@ -1,7 +1,6 @@
import React from "react"; import React from "react";
import { Routes, Route } from "react-router-dom"; import { Routes, Route } from "react-router-dom";
import { BrowserRouter as Router } from 'react-router-dom';
import Login from "./components/Login/Login"; import Login from "./components/Login/Login";
import Dashboard from "./components/Dashboard/dashboard"; import Dashboard from "./components/Dashboard/dashboard";
import UserMaintance from "./components/Dashboard/UserMaintance"; import UserMaintance from "./components/Dashboard/UserMaintance";
@ -12,34 +11,27 @@ import DynamicTable from "./components/Dashboard/Dynamictable";
import Form from "./components/Dashboard/Form"; import Form from "./components/Dashboard/Form";
import ForgotPassword from "./components/Login/ForgotPassword"; import ForgotPassword from "./components/Login/ForgotPassword";
import CreateAccount from "./components/Login/CreateAccount"; import CreateAccount from "./components/Login/CreateAccount";
import Apitest from "./components/Dashboard/Test/Apitest"; import DashboardBuilder from "./components/Dashboard/DashboardBuilder";
const App = () => { const App = () => {
return ( return (
<Routes> <div>
<Route path="/" element={<Login />} /> <Router>
<Route path="/Dashboard" element={<Dashboard />} /> <Routes>
<Route path="/UserGroupMaintance" element={<UserGroupMaintance />} /> <Route path="/" element={<Login />} />
<Route path="/Dashboard/UserMaintance" element={<UserMaintance />} /> <Route path="/Dashboard" element={<Dashboard />} />
<Route path="/CodeExtension" element={<CodeExtension />} /> <Route path="/UserGroupMaintance" element={<UserGroupMaintance />} />
<Route <Route path="/Dashboard/UserMaintance" element={<UserMaintance />} />
path="/Dashboard/DashboardBuilder" <Route path="/CodeExtension" element={<CodeExtension />} />
element={<dashboardBuilder />} <Route path="/Dashboard/DashboardBuilder" element={<DashboardBuilder />} />
/> <Route path="/Extension" element={<Extension />} />
<Route path="/Extension" element={<Extension />} /> <Route path="/Dynamictable" element={<DynamicTable />} />
<Route path="/Dynamictable" element={<DynamicTable />} /> <Route path="/Form" element={<Form />} />
<Route path="/Form" element={<Form />} /> <Route path="/ForgotPassword" element={<ForgotPassword />} />
<Route path="/ForgotPassword" element={<ForgotPassword />} /> <Route path="/CreateAccount" element={<CreateAccount />} />
<Route path="/CreateAccount" element={<CreateAccount />} /> </Routes>
<Route path="/Test" element={<Apitest />} /> </Router>
</div>
{/* buildercomponents */}
</Routes>
); );
}; };

View File

@ -1,197 +0,0 @@
import React, { useState, useEffect } from "react";
import { Button, Checkbox, Container, FormControlLabel, Modal, Table, TextField, Typography } from "@mui/material";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faEdit, faTrashAlt, faPlus } from "@fortawesome/free-solid-svg-icons";
const api = process.env.REACT_APP_API_BASE_URL;
function AccessTypeManagement() {
const [accessTypes, setAccessTypes] = useState([]);
const [showAddEditModal, setShowAddEditModal] = useState(false);
const [currentAccessType, setCurrentAccessType] = useState({
typeId: "",
typeName: "",
description: "",
isActive: false
});
const [isEditing, setIsEditing] = useState(false);
const [recordsPerPage, setRecordsPerPage] = useState(10);
const [visibleColumns, setVisibleColumns] = useState({
typeId: true,
typeName: true,
description: true,
isActive: true,
actions: true
});
useEffect(() => {
const token = localStorage.getItem("token")
const apiUrl = `${api}/api/getAllAccessTypes`;
const fetchAccessTypes = 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();
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 } = event.target;
setCurrentAccessType(prev => ({
...prev,
[name]: name === "isActive" ? checked : value
}));
};
const handleSubmit = (event) => {
event.preventDefault();
if (isEditing) {
setAccessTypes(accessTypes.map(type =>
type.typeId === currentAccessType.typeId ? currentAccessType : type
));
} else {
const newTypeId = `ID${accessTypes.length + 1}`;
setAccessTypes([...accessTypes, { ...currentAccessType, typeId: newTypeId }]);
}
setShowAddEditModal(false);
};
const openModal = (type = { typeId: "", typeName: "", description: "", isActive: false }) => {
setIsEditing(!!type.typeId);
setCurrentAccessType(type);
setShowAddEditModal(true);
};
const handleDelete = (typeId) => {
setAccessTypes(accessTypes.filter(type => type.typeId !== typeId));
};
return (
<Container className="mt-5">
<div style={{ display: "flex", justifyContent: "flex-end", marginBottom: "1rem" }}>
<Button onClick={() => openModal()} variant="contained" startIcon={<FontAwesomeIcon icon={faPlus} />}>
ADD
</Button>
</div>
<div className="table-responsive">
<Table striped bordered hover>
<thead>
<tr>
{Object.entries(visibleColumns).map(([key, visible]) =>
visible ? <th key={key}>{key.charAt(0).toUpperCase() + key.slice(1)}</th> : null
)}
</tr>
</thead>
<tbody>
{accessTypes.slice(0, recordsPerPage).map((type, index) => (
<tr key={index}>
{Object.entries(visibleColumns).map(([key, visible]) =>
visible ? (
<td key={key}>
{key === "actions" ? (
<>
<Button variant="light" size="small" onClick={() => openModal(type)}>
<FontAwesomeIcon icon={faEdit} />
</Button>
<Button variant="light" size="small" onClick={() => handleDelete(type.typeId)}>
<FontAwesomeIcon icon={faTrashAlt} />
</Button>
</>
) : (
key === "isActive" ? (
<FormControlLabel
control={<Checkbox checked={type[key]} onChange={handleInputChange} name={key} />}
label=""
/>
) : (
type[key]
)
)}
</td>
) : null
)}
</tr>
))}
</tbody>
</Table>
</div>
<div style={{ display: "flex", justifyContent: "space-between", marginTop: "1rem" }}>
<div>
<Typography variant="body1">Manage Columns</Typography>
{Object.keys(visibleColumns).map((key) => (
<FormControlLabel
key={key}
control={<Checkbox checked={visibleColumns[key]} onChange={() => toggleColumn(key)} />}
label={key.charAt(0).toUpperCase() + key.slice(1)}
/>
))}
</div>
<div>
<Typography variant="body1">Records Per Page</Typography>
{[10, 20, 30, 50].map((number) => (
<Button key={number} onClick={() => setRecordsPerPage(number)} variant="outlined">
{number}
</Button>
))}
</div>
</div>
<Modal open={showAddEditModal} onClose={() => setShowAddEditModal(false)}>
<div style={{ padding: "1rem", backgroundColor: "white", borderRadius: "8px", maxWidth: "400px", margin: "auto" }}>
<Typography variant="h6" gutterBottom>{isEditing ? "Edit Access Type" : "Add Access Type"}</Typography>
<form onSubmit={handleSubmit}>
<TextField
fullWidth
label="Type Name"
name="typeName"
value={currentAccessType.typeName}
onChange={handleInputChange}
required
style={{ marginBottom: "1rem" }}
/>
<TextField
fullWidth
label="Description"
name="description"
value={currentAccessType.description}
onChange={handleInputChange}
style={{ marginBottom: "1rem" }}
/>
<FormControlLabel
control={<Checkbox checked={currentAccessType.isActive} onChange={handleInputChange} name="isActive" />}
label="Active?"
style={{ marginBottom: "1rem" }}
/>
<div style={{ display: "flex", justifyContent: "flex-end" }}>
<Button variant="outlined" onClick={() => setShowAddEditModal(false)} style={{ marginRight: "1rem" }}>Close</Button>
<Button variant="contained" type="submit">{isEditing ? "Update" : "Add"}</Button>
</div>
</form>
</div>
</Modal>
</Container>
);
}
export default AccessTypeManagement;

View File

@ -1,8 +1,8 @@
import React, { useState, useEffect, useRef } from "react"; import React, { useState, useEffect, useRef } from "react";
import { Box, Button } from "@mui/material"; import { Box, Button } from "@mui/material";
import { DataGrid, GridToolbarContainer } from "@mui/x-data-grid"; import { DataGrid, GridToolbarContainer } from "@mui/x-data-grid";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { BsThreeDotsVertical } from "react-icons/bs"; // Importing react-icons
import { faEllipsisV } from "@fortawesome/free-solid-svg-icons";
const api = process.env.REACT_APP_API_BASE_URL; const api = process.env.REACT_APP_API_BASE_URL;
@ -34,6 +34,7 @@ function CustomToolbar({ apiRef, handleModal }) {
function ApiRegistery() { function ApiRegistery() {
const [menuItems, setMenuItems] = useState([]); const [menuItems, setMenuItems] = useState([]);
const [selectedMenuItem, setSelectedMenuItem] = useState(null); const [selectedMenuItem, setSelectedMenuItem] = useState(null);
// eslint-disable-next-line
const [isModalOpen, setIsModalOpen] = useState(false); const [isModalOpen, setIsModalOpen] = useState(false);
const apiRef = useRef(null); const apiRef = useRef(null);
@ -88,7 +89,7 @@ function ApiRegistery() {
className="cursor-pointer" className="cursor-pointer"
onClick={() => handleThreeDotsClick(row.id)} onClick={() => handleThreeDotsClick(row.id)}
> >
<FontAwesomeIcon icon={faEllipsisV} /> <BsThreeDotsVertical /> {/* Using react-icons */}
</div> </div>
{selectedMenuItem === row.id && ( {selectedMenuItem === row.id && (
<div className="absolute right-0 mt-2 py-2 w-48 bg-white rounded-lg shadow-xl"> <div className="absolute right-0 mt-2 py-2 w-48 bg-white rounded-lg shadow-xl">

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect, useRef } from "react"; import React, { useState, useEffect } from "react";
import { import {
Box, Box,
Button, Button,
@ -13,8 +13,7 @@ import {
Autocomplete, Autocomplete,
} from "@mui/material"; } from "@mui/material";
import { DataGrid, GridToolbarContainer } from "@mui/x-data-grid"; import { DataGrid, GridToolbarContainer } from "@mui/x-data-grid";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FaEllipsisV } from "react-icons/fa"; // Importing react-icons instead of Font Awesome
import { faEllipsisV } from "@fortawesome/free-solid-svg-icons";
import AirplanemodeActiveIcon from "@mui/icons-material/AirplanemodeActive"; import AirplanemodeActiveIcon from "@mui/icons-material/AirplanemodeActive";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import Extension from "./Extension"; import Extension from "./Extension";
@ -45,7 +44,7 @@ function CodeExtension() {
useEffect(() => { useEffect(() => {
const fetchData = async () => { const fetchData = async () => {
const token = localStorage.getItem("token"); // Get token from local storage const token = localStorage.getItem("authToken"); // Get token from local storage
try { try {
const response = await fetch(`${process.env.REACT_APP_API_BASE_URL}/api/extension`, { const response = await fetch(`${process.env.REACT_APP_API_BASE_URL}/api/extension`, {
headers: { headers: {
@ -105,7 +104,7 @@ function CodeExtension() {
className="cursor-pointer" className="cursor-pointer"
onClick={() => handleThreeDotsClick(row.id)} onClick={() => handleThreeDotsClick(row.id)}
> >
<FontAwesomeIcon icon={faEllipsisV} /> <FaEllipsisV /> {/* Using react-icons instead of Font Awesome */}
</div> </div>
{selectedMenuItem === row.id && ( {selectedMenuItem === row.id && (
<div className="absolute right-0 mt-2 py-2 w-48 bg-white rounded-lg shadow-xl"> <div className="absolute right-0 mt-2 py-2 w-48 bg-white rounded-lg shadow-xl">

View File

@ -35,7 +35,7 @@ const DynamicTable = () => {
}, [location.state]); }, [location.state]);
const fetchForms = async () => { const fetchForms = async () => {
const token = localStorage.getItem("token"); // Get token from local storage const token = localStorage.getItem("authToken"); // Get token from local storage
try { try {
const response = await axios.get(`${api}/api/form_setup`, { const response = await axios.get(`${api}/api/form_setup`, {
headers: { headers: {
@ -55,7 +55,7 @@ const DynamicTable = () => {
}; };
const handleDelete = async (id) => { const handleDelete = async (id) => {
const token = localStorage.getItem("token"); // Get token from local storage const token = localStorage.getItem("authToken"); // Get token from local storage
try { try {
await axios.delete(`${api}/api/form_setup/${id}`, { await axios.delete(`${api}/api/form_setup/${id}`, {
headers: { headers: {
@ -69,7 +69,7 @@ const DynamicTable = () => {
}; };
const handleBuild = async (id) => { const handleBuild = async (id) => {
const token = localStorage.getItem("token"); // Get token from local storage const token = localStorage.getItem("authToken"); // Get token from local storage
try { try {
await axios.post( await axios.post(
`${api}/api/dynamic_form_build`, `${api}/api/dynamic_form_build`,

View File

@ -23,7 +23,7 @@ const Extension = ({ onSubmit }) => {
onSubmit(formData.dataType); onSubmit(formData.dataType);
} }
// Navigate to CodeExtension page with the form data // Navigate to CodeExtension page with the form data
navigate('/Codeextension', { state: { formData } }); navigate('/CodeExtension', { state: { formData } });
}; };
return ( return (

View File

@ -2,7 +2,7 @@ import React, { useState } from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { v4 as uuidv4 } from "uuid"; import { v4 as uuidv4 } from "uuid";
function DynamicForm() { function Form() {
const [components, setComponents] = useState([ const [components, setComponents] = useState([
{ {
id: uuidv4(), id: uuidv4(),
@ -214,4 +214,4 @@ function DynamicForm() {
); );
} }
export default DynamicForm; export default Form;

View File

@ -1,34 +1,110 @@
// HomePage.js // HomePage.js
import React from 'react'; import React from 'react';
import { BarChart } from '@mui/x-charts/BarChart'; // Import BarChart component import { FaUsers, FaCog, FaChartBar, FaShieldAlt } from 'react-icons/fa';
const Card = ({ index }) => { const StatCard = ({ icon: Icon, title, value, color }) => (
return ( <div className="bg-white rounded-xl shadow-lg p-6 hover:shadow-xl transition-all duration-200">
<div className="bg-white border border-gray-300 rounded-lg shadow-md p-6 m-4 cursor-pointer transition-transform transform hover:scale-105 hover:shadow-lg flex flex-col items-center justify-center text-center"> <div className="flex items-center space-x-4">
<h3 className="text-lg font-semibold mb-2">INDEX {index}</h3> <div className={`p-3 rounded-lg ${color}`}>
<p className="text-gray-600">{index}.</p> <Icon className="w-6 h-6 text-white" />
</div>
<div>
<h3 className="text-gray-500 text-sm font-medium">{title}</h3>
<p className="text-2xl font-bold text-gray-800">{value}</p>
</div>
</div> </div>
); </div>
}; );
const HomePage = () => { const HomePage = () => {
return ( return (
<div className="min-h-screen flex flex-col items-center justify-center bg-gray-100 p-4"> <div className="space-y-6">
<h2 className="text-3xl font-semibold text-gray-700 mb-6">Welcome to the Dashboard!</h2> <div className="flex items-center justify-between">
<div className="flex flex-wrap justify-center"> <h1 className="text-3xl font-bold text-gray-800">Welcome Back!</h1>
<Card index={1} /> <div className="text-sm text-gray-500">Last updated: {new Date().toLocaleDateString()}</div>
<Card index={2} />
<Card index={3} />
</div> </div>
<div className="w-full mt-8 flex justify-center">
{/* Add BarChart component */} {/* Stats Grid */}
<BarChart <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
xAxis={[{ scaleType: 'band', data: ['group A', 'group B', 'group C'] }]} <StatCard
series={[{ data: [4, 3, 5] }, { data: [1, 6, 3] }, { data: [2, 5, 6] }]} icon={FaUsers}
width={700} title="Total Users"
height={400} value="1,234"
color="bg-gradient-to-r from-purple-500 to-indigo-500"
/> />
<StatCard
icon={FaCog}
title="Active Systems"
value="12"
color="bg-gradient-to-r from-blue-500 to-cyan-500"
/>
<StatCard
icon={FaChartBar}
title="Reports Generated"
value="456"
color="bg-gradient-to-r from-indigo-500 to-purple-500"
/>
<StatCard
icon={FaShieldAlt}
title="Security Score"
value="98%"
color="bg-gradient-to-r from-green-500 to-emerald-500"
/>
</div>
{/* Recent Activity */}
<div className="bg-white rounded-xl shadow-lg p-6">
<h2 className="text-xl font-semibold text-gray-800 mb-4">Recent Activity</h2>
<div className="space-y-4">
{[1, 2, 3].map((item) => (
<div key={item} className="flex items-center space-x-4 p-4 rounded-lg hover:bg-gray-50 transition-colors">
<div className="w-2 h-2 rounded-full bg-purple-500"></div>
<div className="flex-1">
<p className="text-gray-800">System update completed</p>
<p className="text-sm text-gray-500">2 hours ago</p>
</div>
</div>
))}
</div>
</div>
{/* Quick Actions */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="bg-gradient-to-br from-purple-500 to-indigo-600 rounded-xl shadow-lg p-6 text-white">
<h2 className="text-xl font-semibold mb-4">Quick Actions</h2>
<div className="space-y-3">
<button className="w-full bg-white/10 hover:bg-white/20 text-white font-medium py-2 px-4 rounded-lg transition-colors">
Generate Report
</button>
<button className="w-full bg-white/10 hover:bg-white/20 text-white font-medium py-2 px-4 rounded-lg transition-colors">
Add New User
</button>
<button className="w-full bg-white/10 hover:bg-white/20 text-white font-medium py-2 px-4 rounded-lg transition-colors">
System Settings
</button>
</div>
</div>
<div className="bg-gradient-to-br from-blue-500 to-cyan-600 rounded-xl shadow-lg p-6 text-white">
<h2 className="text-xl font-semibold mb-4">System Status</h2>
<div className="space-y-4">
<div className="flex items-center justify-between">
<span>CPU Usage</span>
<span className="font-medium">45%</span>
</div>
<div className="w-full bg-white/20 rounded-full h-2">
<div className="bg-white h-2 rounded-full" style={{ width: '45%' }}></div>
</div>
<div className="flex items-center justify-between">
<span>Memory Usage</span>
<span className="font-medium">62%</span>
</div>
<div className="w-full bg-white/20 rounded-full h-2">
<div className="bg-white h-2 rounded-full" style={{ width: '62%' }}></div>
</div>
</div>
</div>
</div> </div>
</div> </div>
); );

View File

@ -1,8 +1,8 @@
// eslint-disable-next-line
import React, { useState, useEffect, useRef } from 'react'; import React, { useState, useEffect, useRef } from 'react';
import { Box, Button } from '@mui/material'; import { Box, Button } from '@mui/material';
import { DataGrid, GridToolbarContainer } from '@mui/x-data-grid'; import { DataGrid, GridToolbarContainer } from '@mui/x-data-grid';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { BsThreeDotsVertical } from 'react-icons/bs'; // Importing react-icons
import { faEllipsisV } from '@fortawesome/free-solid-svg-icons';
function CustomToolbar({ apiRef, handleThreeDotsClick, handleModal }) { function CustomToolbar({ apiRef, handleThreeDotsClick, handleModal }) {
const handleGoToPage1 = () => { const handleGoToPage1 = () => {
@ -24,17 +24,16 @@ function MenuAccessControl() {
const [selectedMenuItem, setSelectedMenuItem] = useState(null); const [selectedMenuItem, setSelectedMenuItem] = useState(null);
const [isModalOpen, setIsModalOpen] = useState(false); const [isModalOpen, setIsModalOpen] = useState(false);
const [isUpdateModalOpen, setIsUpdateModalOpen] = useState(false); const [isUpdateModalOpen, setIsUpdateModalOpen] = useState(false);
const [newMenuItem, setNewMenuItem] = useState({ // const [newMenuItem, setNewMenuItem] = useState({
// Define initial values for the new menu item in the modal // No: '',
No: '', // menuItemname: '',
menuItemname: '', // view: '',
view: '', // create: '',
create: '', // edit: '',
edit: '', // delete: '',
delete: '', // query: '',
query: '', // export: [],
export: [], // });
});
const apiRef = useRef(null); const apiRef = useRef(null);
useEffect(() => { useEffect(() => {
@ -72,15 +71,15 @@ function MenuAccessControl() {
setIsModalOpen(true); setIsModalOpen(true);
}; };
const handleModalSave = () => { // const handleModalSave = () => {
// Implement save logic for adding a new menu item here // // Implement save logic for adding a new menu item here
setIsModalOpen(false); // setIsModalOpen(false);
}; // };
const handleUpdateSave = () => { // const handleUpdateSave = () => {
// Implement save logic for updating a menu item here // // Implement save logic for updating a menu item here
setIsUpdateModalOpen(false); // setIsUpdateModalOpen(false);
}; // };
const columns = [ const columns = [
{ field: 'No', headerName: 'No', width: 100,headerClassName: 'custom-header', cellClassName: 'custom-cell' }, { field: 'No', headerName: 'No', width: 100,headerClassName: 'custom-header', cellClassName: 'custom-cell' },
@ -98,7 +97,7 @@ function MenuAccessControl() {
renderCell: ({ row }) => ( renderCell: ({ row }) => (
<div> <div>
<div className="three-dots" onClick={() => handleThreeDotsClick(row.menuItemId)}> <div className="three-dots" onClick={() => handleThreeDotsClick(row.menuItemId)}>
<FontAwesomeIcon icon={faEllipsisV} /> <BsThreeDotsVertical /> {/* Using react-icons */}
</div> </div>
{selectedMenuItem === row.menuItemId && ( {selectedMenuItem === row.menuItemId && (
<div className="popover"> <div className="popover">

View File

@ -1,8 +1,7 @@
import React, { useState, useEffect, useRef } from "react"; import React, { useState, useEffect, useRef } from "react";
import { Box, Button } from "@mui/material"; import { Box, Button } from "@mui/material";
import { DataGrid, GridToolbarContainer } from "@mui/x-data-grid"; import { DataGrid, GridToolbarContainer } from "@mui/x-data-grid";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { BsThreeDotsVertical } from "react-icons/bs"; // Importing react-icons
import { faEllipsisV } from "@fortawesome/free-solid-svg-icons";
import "./MenuMaintance.css"; import "./MenuMaintance.css";
const api = process.env.REACT_APP_API_BASE_URL; const api = process.env.REACT_APP_API_BASE_URL;
@ -30,7 +29,7 @@ function MenuMaintenance() {
useEffect(() => { useEffect(() => {
const fetchData = async () => { const fetchData = async () => {
const token = localStorage.getItem("token"); const token = localStorage.getItem("authToken");
try { try {
const response = await fetch(`${api}/api1/submenu1`, { const response = await fetch(`${api}/api1/submenu1`, {
headers: { headers: {
@ -112,8 +111,7 @@ function MenuMaintenance() {
className="three-dots" className="three-dots"
onClick={() => handleThreeDotsClick(row.menuItemId)} onClick={() => handleThreeDotsClick(row.menuItemId)}
> >
<FontAwesomeIcon <BsThreeDotsVertical
icon={faEllipsisV}
className="cursor-pointer text-gray-800 hover:text-gray-600" className="cursor-pointer text-gray-800 hover:text-gray-600"
/> />
</div> </div>
@ -160,7 +158,7 @@ function MenuMaintenance() {
fontWeight: "bold", fontWeight: "bold",
}, },
}} }}
className=" border border-gray-200 shadow-lg rounded-lg bg-gray-400" className="border border-gray-200 shadow-lg rounded-lg bg-gray-400"
/> />
</Box> </Box>
{/* Your modals and other components */} {/* Your modals and other components */}

View File

@ -37,6 +37,12 @@ const Report = () => {
}, },
}); });
if (response.status === 401) {
console.error("Unauthorized. Redirecting to login.");
// Redirect to the login page here if needed
return;
}
if (!response.ok) { if (!response.ok) {
throw new Error("Failed to fetch data"); throw new Error("Failed to fetch data");
} }

View File

@ -1,20 +1,18 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FaUser, FaUsers, FaUtensils, FaLock, FaCogs, FaKey } from 'react-icons/fa';
import { faUser, faUsers, faUtensils, faLock, faCogs, faKey } from '@fortawesome/free-solid-svg-icons';
import UserMaintance from './UserMaintance'; import UserMaintance from './UserMaintance';
import UserGroupMaintance from './UserGroupMaintance/UserGroupMaintance'; import UserGroupMaintance from './UserGroupMaintance/UserGroupMaintance';
import MenuMaintance from './MenuMaintance/MenuMaintance'; import MenuMaintance from './MenuMaintance/MenuMaintance';
import MenuAccessControl from './MenuAccessControl/MenuAccessControl'; import MenuAccessControl from './MenuAccessControl/MenuAccessControl';
import SystemParameters from './SystemParameters/SystemParameters'; import SystemParameters from './SystemParameters/SystemParameters';
import AccessType from './AccessType/AccessType';
import ApiRegistery from './ApiRegistery/ApiRegistery'; import ApiRegistery from './ApiRegistery/ApiRegistery';
import TokenRegistery from './TokenRegistery/TokenRegistery'; import TokenRegistery from './TokenRegistery/TokenRegistery';
import Codeextension from './Codeextension.js'; import Codeextension from './Codeextension.js';
import DynamicTable from './Dynamictable.js'; import DynamicTable from './Dynamictable.js';
const Card = ({ title, content, icon, onClick }) => ( const Card = ({ title, content, icon: Icon, onClick }) => (
<div onClick={onClick} className="bg-white border border-gray-300 rounded-lg shadow-md p-6 m-4 cursor-pointer transition-transform transform hover:scale-105 hover:shadow-lg flex flex-col items-center justify-center text-center"> <div onClick={onClick} className="bg-white border border-gray-300 rounded-lg shadow-md p-6 m-4 cursor-pointer transition-transform transform hover:scale-105 hover:shadow-lg flex flex-col items-center justify-center text-center">
<FontAwesomeIcon icon={icon} className="text-4xl text-gray-800 mb-4" /> <Icon className="text-4xl text-gray-800 mb-4" />
<h3 className="text-lg font-semibold">{title}</h3> <h3 className="text-lg font-semibold">{title}</h3>
<p className="text-gray-600">{content}</p> <p className="text-gray-600">{content}</p>
</div> </div>
@ -26,7 +24,7 @@ const CardList = () => {
const [showMenuMaintance, setShowMenuMaintance] = useState(false); const [showMenuMaintance, setShowMenuMaintance] = useState(false);
const [showMenuAccessControl, setShowMenuAccessControl] = useState(false); const [showMenuAccessControl, setShowMenuAccessControl] = useState(false);
const [showSystemParameters, setShowSystemParameters] = useState(false); const [showSystemParameters, setShowSystemParameters] = useState(false);
const [showAccessType, setShowAccessType] = useState(false); // const [showAccessType, setShowAccessType] = useState(false);
const [showApiRegistery, setShowApiRegistery] = useState(false); const [showApiRegistery, setShowApiRegistery] = useState(false);
const [showTokenRegistery, setShowTokenRegistery] = useState(false); const [showTokenRegistery, setShowTokenRegistery] = useState(false);
const [showCodeExtension, setShowCodeExtension] = useState(false); const [showCodeExtension, setShowCodeExtension] = useState(false);
@ -38,7 +36,7 @@ const CardList = () => {
setShowMenuMaintance(menuItemDesc === 'Menu Maintance'); setShowMenuMaintance(menuItemDesc === 'Menu Maintance');
setShowMenuAccessControl(menuItemDesc === 'Menu Access Control'); setShowMenuAccessControl(menuItemDesc === 'Menu Access Control');
setShowSystemParameters(menuItemDesc === 'System Parameters'); setShowSystemParameters(menuItemDesc === 'System Parameters');
setShowAccessType(menuItemDesc === 'Access Type'); // setShowAccessType(menuItemDesc === 'Access Type');
setShowApiRegistery(menuItemDesc === 'Api Registery'); setShowApiRegistery(menuItemDesc === 'Api Registery');
setShowTokenRegistery(menuItemDesc === 'Token Registery'); setShowTokenRegistery(menuItemDesc === 'Token Registery');
setShowCodeExtension(menuItemDesc === 'Code Extension'); setShowCodeExtension(menuItemDesc === 'Code Extension');
@ -47,19 +45,19 @@ const CardList = () => {
return ( return (
<> <>
{!showUserMaintance && !showUserGroupMaintance && !showMenuMaintance && !showMenuAccessControl && !showSystemParameters && !showAccessType && !showApiRegistery && !showTokenRegistery && !showCodeExtension && !showDynamicTable && ( {!showUserMaintance && !showUserGroupMaintance && !showMenuMaintance && !showMenuAccessControl && !showSystemParameters && !showApiRegistery && !showTokenRegistery && !showCodeExtension && !showDynamicTable && (
<div className="min-h-screen flex items-center justify-center bg-gray-100 p-4"> <div className="min-h-screen flex items-center justify-center bg-gray-100 p-4">
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-3 xl:grid-cols-4 gap-6"> <div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-3 xl:grid-cols-4 gap-6">
<Card title="User Maintance" content="Manage users" icon={faUser} onClick={() => handleCardClick('User Maintance')} /> <Card title="User Maintance" content="Manage users" icon={FaUser} onClick={() => handleCardClick('User Maintance')} />
<Card title="User Group Maintance" content="Manage user groups" icon={faUsers} onClick={() => handleCardClick('User Group Maintance')} /> <Card title="User Group Maintance" content="Manage user groups" icon={FaUsers} onClick={() => handleCardClick('User Group Maintance')} />
<Card title="Menu Maintance" content="Manage menus" icon={faUtensils} onClick={() => handleCardClick('Menu Maintance')} /> <Card title="Menu Maintance" content="Manage menus" icon={FaUtensils} onClick={() => handleCardClick('Menu Maintance')} />
<Card title="Menu Access Control" content="Control menu access" icon={faLock} onClick={() => handleCardClick('Menu Access Control')} /> <Card title="Menu Access Control" content="Control menu access" icon={FaLock} onClick={() => handleCardClick('Menu Access Control')} />
<Card title="System Parameters" content="Configure system parameters" icon={faCogs} onClick={() => handleCardClick('System Parameters')} /> <Card title="System Parameters" content="Configure system parameters" icon={FaCogs} onClick={() => handleCardClick('System Parameters')} />
<Card title="Access Type" content="Manage access types" icon={faKey} onClick={() => handleCardClick('Access Type')} /> {/* <Card title="Access Type" content="Manage access types" icon={FaKey} onClick={() => handleCardClick('Access Type')} /> */}
<Card title="Api Registery" content="Manage APIs" icon={faUser} onClick={() => handleCardClick('Api Registery')} /> <Card title="Api Registery" content="Manage APIs" icon={FaUser} onClick={() => handleCardClick('Api Registery')} />
<Card title="Token Registery" content="Manage tokens" icon={faKey} onClick={() => handleCardClick('Token Registery')} /> <Card title="Token Registery" content="Manage tokens" icon={FaKey} onClick={() => handleCardClick('Token Registery')} />
<Card title="Code Extension" content="Extend code functionalities" icon={faLock} onClick={() => handleCardClick('Code Extension')} /> <Card title="Code Extension" content="Extend code functionalities" icon={FaLock} onClick={() => handleCardClick('Code Extension')} />
<Card title="Dynamic Table" content="Dynamic data tables" icon={faKey} onClick={() => handleCardClick('Dynamic Table')} /> <Card title="Dynamic Table" content="Dynamic data tables" icon={FaKey} onClick={() => handleCardClick('Dynamic Table')} />
</div> </div>
</div> </div>
)} )}
@ -68,7 +66,7 @@ const CardList = () => {
{showMenuMaintance && <MenuMaintance />} {showMenuMaintance && <MenuMaintance />}
{showMenuAccessControl && <MenuAccessControl />} {showMenuAccessControl && <MenuAccessControl />}
{showSystemParameters && <SystemParameters />} {showSystemParameters && <SystemParameters />}
{showAccessType && <AccessType />} {/* {showAccessType && <AccessType />} */}
{showApiRegistery && <ApiRegistery />} {showApiRegistery && <ApiRegistery />}
{showTokenRegistery && <TokenRegistery />} {showTokenRegistery && <TokenRegistery />}
{showCodeExtension && <Codeextension />} {showCodeExtension && <Codeextension />}

View File

@ -1,427 +0,0 @@
import React, { useEffect, useState } from "react";
import axios from "axios";
import {
Button,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
Snackbar,
Alert,
Typography,
TextField,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
TablePagination,
Paper,
IconButton,
InputAdornment
} from "@mui/material";
import { makeStyles } from '@mui/styles';
import SearchIcon from '@mui/icons-material/Search';
const API_URL = "http://34.198.218.30:30179/entityBuilder/Gaurav_testing";
const token = localStorage.getItem("authToken");
// Custom styles using makeStyles
const useStyles = makeStyles((theme) => ({
tableHeader: {
backgroundColor: "#000000",
color: "#ffffff",
},
searchContainer: {
display: 'flex',
alignItems: 'center',
marginBottom: theme.spacing(2),
},
searchInput: {
marginRight: theme.spacing(2),
flex: 1,
},
tableContainer: {
marginTop: theme.spacing(2),
},
dialogContent: {
display: 'flex',
flexDirection: 'column',
gap: theme.spacing(2),
},
formControl: {
marginBottom: theme.spacing(2),
},
button: {
margin: theme.spacing(1),
},
}));
const EntityTable = () => {
const classes = useStyles();
const [data, setData] = useState([]);
const [filteredData, setFilteredData] = useState([]);
const [newEntity, setNewEntity] = useState({
name: "",
email: "",
mobno: "",
address: "",
pincode: "",
});
const [editEntity, setEditEntity] = useState(null);
const [showEditModal, setShowEditModal] = useState(false);
const [showAddModal, setShowAddModal] = useState(false);
const [showDeleteModal, setShowDeleteModal] = useState(false);
const [deleteEntityId, setDeleteEntityId] = useState(null);
const [currentPage, setCurrentPage] = useState(0);
const [itemsPerPage] = useState(5); // Adjust this value as needed
const [searchQuery, setSearchQuery] = useState("");
const [openSnackbar, setOpenSnackbar] = useState(false);
const [snackbarMessage, setSnackbarMessage] = useState("");
const [snackbarSeverity, setSnackbarSeverity] = useState("success");
const handlePageChange = (event, newPage) => {
setCurrentPage(newPage);
};
useEffect(() => {
fetchData();
}, []);
useEffect(() => {
handleSearch();
}, [searchQuery, data]);
const fetchData = async () => {
try {
const response = await axios.get(API_URL, {
headers: { Authorization: `Bearer ${token}` },
});
setData(response.data);
} catch (error) {
console.error("Error fetching data:", error);
}
};
const handleDelete = async () => {
try {
await axios.delete(`${API_URL}/${deleteEntityId}`, {
headers: { Authorization: `Bearer ${token}` },
});
fetchData();
setSnackbarMessage("Successfully deleted!");
setSnackbarSeverity("success");
setOpenSnackbar(true);
setShowDeleteModal(false);
} catch (error) {
console.error("Error deleting data:", error);
setSnackbarMessage("Failed to delete!");
setSnackbarSeverity("error");
setOpenSnackbar(true);
}
};
const handleAdd = async () => {
try {
await axios.post(API_URL, newEntity, {
headers: { Authorization: `Bearer ${token}` },
});
fetchData();
setNewEntity({
name: "",
email: "",
mobno: "",
address: "",
pincode: "",
});
setShowAddModal(false);
setSnackbarMessage("Successfully added!");
setSnackbarSeverity("success");
setOpenSnackbar(true);
} catch (error) {
console.error("Error adding data:", error);
setSnackbarMessage("Failed to add!");
setSnackbarSeverity("error");
setOpenSnackbar(true);
}
};
const handleChange = (e) => {
const { name, value } = e.target;
setNewEntity({ ...newEntity, [name]: value });
};
const handleEditChange = (e) => {
const { name, value } = e.target;
setEditEntity({ ...editEntity, [name]: value });
};
const handleEdit = (entity) => {
setEditEntity(entity);
setShowEditModal(true);
};
const handleUpdate = async () => {
try {
await axios.put(`${API_URL}/${editEntity.id}`, editEntity, {
headers: { Authorization: `Bearer ${token}` },
});
fetchData();
setShowEditModal(false);
setSnackbarMessage("Successfully updated!");
setSnackbarSeverity("success");
setOpenSnackbar(true);
} catch (error) {
console.error("Error updating data:", error);
setSnackbarMessage("Failed to update!");
setSnackbarSeverity("error");
setOpenSnackbar(true);
}
};
const handleSearch = () => {
const filtered = data.filter(
(entity) =>
entity.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
entity.email.toLowerCase().includes(searchQuery.toLowerCase()) ||
entity.mobno.toLowerCase().includes(searchQuery.toLowerCase()) ||
entity.address.toLowerCase().includes(searchQuery.toLowerCase()) ||
entity.pincode.toLowerCase().includes(searchQuery.toLowerCase())
);
setFilteredData(filtered);
};
return (
<div className="container mt-5">
<Typography variant="h4" gutterBottom>
Entity Table
</Typography>
<div className={classes.searchContainer}>
<Button
variant="contained"
color="primary"
onClick={() => setShowAddModal(true)}
className={classes.button}
>
Add Entity
</Button>
<TextField
className={classes.searchInput}
label="Search"
variant="outlined"
size="small"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton>
<SearchIcon />
</IconButton>
</InputAdornment>
),
}}
/>
</div>
<TableContainer component={Paper} className={classes.tableContainer}>
<Table>
<TableHead>
<TableRow className={classes.tableHeader}>
<TableCell>ID</TableCell>
<TableCell>Name</TableCell>
<TableCell>Email</TableCell>
<TableCell>Mobile No</TableCell>
<TableCell>Address</TableCell>
<TableCell>Pincode</TableCell>
<TableCell>Actions</TableCell>
</TableRow>
</TableHead>
<TableBody>
{filteredData.slice(currentPage * itemsPerPage, (currentPage + 1) * itemsPerPage).map((entity) => (
<TableRow key={entity.id}>
<TableCell>{entity.id}</TableCell>
<TableCell>{entity.name}</TableCell>
<TableCell>{entity.email}</TableCell>
<TableCell>{entity.mobno}</TableCell>
<TableCell>{entity.address}</TableCell>
<TableCell>{entity.pincode}</TableCell>
<TableCell>
<Button
variant="contained"
color="warning"
size="small"
className={classes.button}
onClick={() => handleEdit(entity)}
>
Update
</Button>
<Button
variant="contained"
color="error"
size="small"
className={classes.button}
onClick={() => {
setDeleteEntityId(entity.id);
setShowDeleteModal(true);
}}
>
Delete
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
<TablePagination
rowsPerPageOptions={[5, 10, 25]}
component="div"
count={filteredData.length}
rowsPerPage={itemsPerPage}
page={currentPage}
onPageChange={handlePageChange}
/>
<Dialog open={showEditModal} onClose={() => setShowEditModal(false)}>
<DialogTitle>Edit Entity</DialogTitle>
<DialogContent className={classes.dialogContent}>
<TextField
label="Name"
variant="outlined"
fullWidth
name="name"
value={editEntity?.name || ""}
onChange={handleEditChange}
className={classes.formControl}
/>
<TextField
label="Email"
variant="outlined"
fullWidth
name="email"
value={editEntity?.email || ""}
onChange={handleEditChange}
className={classes.formControl}
/>
<TextField
label="Mobile No"
variant="outlined"
fullWidth
name="mobno"
value={editEntity?.mobno || ""}
onChange={handleEditChange}
className={classes.formControl}
/>
<TextField
label="Address"
variant="outlined"
fullWidth
name="address"
value={editEntity?.address || ""}
onChange={handleEditChange}
className={classes.formControl}
/>
<TextField
label="Pincode"
variant="outlined"
fullWidth
name="pincode"
value={editEntity?.pincode || ""}
onChange={handleEditChange}
className={classes.formControl}
/>
</DialogContent>
<DialogActions>
<Button onClick={() => setShowEditModal(false)} color="primary">
Cancel
</Button>
<Button onClick={handleUpdate} color="primary">
Update
</Button>
</DialogActions>
</Dialog>
<Dialog open={showAddModal} onClose={() => setShowAddModal(false)}>
<DialogTitle>Add Entity</DialogTitle>
<DialogContent className={classes.dialogContent}>
<TextField
label="Name"
variant="outlined"
fullWidth
name="name"
value={newEntity.name}
onChange={handleChange}
className={classes.formControl}
/>
<TextField
label="Email"
variant="outlined"
fullWidth
name="email"
value={newEntity.email}
onChange={handleChange}
className={classes.formControl}
/>
<TextField
label="Mobile No"
variant="outlined"
fullWidth
name="mobno"
value={newEntity.mobno}
onChange={handleChange}
className={classes.formControl}
/>
<TextField
label="Address"
variant="outlined"
fullWidth
name="address"
value={newEntity.address}
onChange={handleChange}
className={classes.formControl}
/>
<TextField
label="Pincode"
variant="outlined"
fullWidth
name="pincode"
value={newEntity.pincode}
onChange={handleChange}
className={classes.formControl}
/>
</DialogContent>
<DialogActions>
<Button onClick={() => setShowAddModal(false)} color="primary">
Cancel
</Button>
<Button onClick={handleAdd} color="primary">
Add
</Button>
</DialogActions>
</Dialog>
<Dialog open={showDeleteModal} onClose={() => setShowDeleteModal(false)}>
<DialogTitle>Confirm Delete</DialogTitle>
<DialogContent>
<Typography>Are you sure you want to delete this entity?</Typography>
</DialogContent>
<DialogActions>
<Button onClick={() => setShowDeleteModal(false)} color="primary">
Cancel
</Button>
<Button onClick={handleDelete} color="primary">
Delete
</Button>
</DialogActions>
</Dialog>
<Snackbar
open={openSnackbar}
autoHideDuration={6000}
onClose={() => setOpenSnackbar(false)}
>
<Alert onClose={() => setOpenSnackbar(false)} severity={snackbarSeverity}>
{snackbarMessage}
</Alert>
</Snackbar>
</div>
);
};
export default EntityTable;

View File

@ -1,8 +1,7 @@
import React, { useState, useEffect, useRef } from "react"; import React, { useState, useEffect, useRef } from "react";
import { Box, Button } from "@mui/material"; import { Box, Button } from "@mui/material";
import { DataGrid, GridToolbarContainer } from "@mui/x-data-grid"; import { DataGrid, GridToolbarContainer } from "@mui/x-data-grid";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { BsThreeDotsVertical } from "react-icons/bs"; // Importing react-icons
import { faEllipsisV } from "@fortawesome/free-solid-svg-icons";
const api = process.env.REACT_APP_API_BASE_URL; const api = process.env.REACT_APP_API_BASE_URL;
@ -31,10 +30,10 @@ function CustomToolbar({ apiRef, handleModal }) {
); );
} }
function TokenRegistery() { function TokenRegistry() {
const [menuItems, setMenuItems] = useState([]); const [menuItems, setMenuItems] = useState([]);
const [selectedMenuItem, setSelectedMenuItem] = useState(null); const [selectedMenuItem, setSelectedMenuItem] = useState(null);
const [isModalOpen, setIsModalOpen] = useState(false); const [, setIsModalOpen] = useState(false);
const apiRef = useRef(null); const apiRef = useRef(null);
useEffect(() => { useEffect(() => {
@ -95,7 +94,7 @@ function TokenRegistery() {
className="cursor-pointer" className="cursor-pointer"
onClick={() => handleThreeDotsClick(row.id)} onClick={() => handleThreeDotsClick(row.id)}
> >
<FontAwesomeIcon icon={faEllipsisV} /> <BsThreeDotsVertical /> {/* Updated icon from react-icons */}
</div> </div>
{selectedMenuItem === row.id && ( {selectedMenuItem === row.id && (
<div className="absolute right-0 mt-2 py-2 w-48 bg-white rounded-lg shadow-xl"> <div className="absolute right-0 mt-2 py-2 w-48 bg-white rounded-lg shadow-xl">
@ -143,4 +142,4 @@ function TokenRegistery() {
); );
} }
export default TokenRegistery; export default TokenRegistry;

View File

@ -16,26 +16,26 @@ const UpdateModal = ({ user, onUpdate, onClose }) => {
onClose(); onClose();
}; };
const handleUpdate = () => { // const handleUpdate = () => {
console.log('Before update:', updatedUser); // console.log('Before update:', updatedUser);
onUpdate(updatedUser); // onUpdate(updatedUser);
onClose(); // onClose();
// Use a callback function with setUpdatedUser to ensure the state is updated // // Use a callback function with setUpdatedUser to ensure the state is updated
setUpdatedUser((prev) => { // setUpdatedUser((prev) => {
const updatedData = { ...prev }; // const updatedData = { ...prev };
console.log('Updated data:', updatedData); // console.log('Updated data:', updatedData);
onUpdate(updatedData); // Pass the updatedData to onUpdate // onUpdate(updatedData); // Pass the updatedData to onUpdate
onClose(); // onClose();
return updatedData; // Return the updatedData to setUpdatedUser // return updatedData; // Return the updatedData to setUpdatedUser
}); // });
// Set the updatedUser state directly to ensure the UI reflects the changes // // Set the updatedUser state directly to ensure the UI reflects the changes
setUpdatedUser(updatedUser); // setUpdatedUser(updatedUser);
}; // };
return ( return (
<div className="modalWrapper"> <div className="modalWrapper">

View File

@ -1,14 +1,12 @@
import React, { useState, useEffect, useRef } from "react"; import React, { useState, useEffect, useRef } from "react";
import { Box, Button } from "@mui/material"; import { Box, Button } from "@mui/material";
import { DataGrid, GridToolbarContainer } from "@mui/x-data-grid"; import { DataGrid, GridToolbarContainer } from "@mui/x-data-grid";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { BsThreeDotsVertical } from "react-icons/bs";
import { faEllipsisV } from "@fortawesome/free-solid-svg-icons";
import "./UserGroupMaintance.css"; import "./UserGroupMaintance.css";
import { Token } from "@mui/icons-material"; // eslint-disable-next-line
const api = process.env.REACT_APP_API_BASE_URL; const api = process.env.REACT_APP_API_BASE_URL;
function CustomToolbar({ apiRef, handleThreeDotsClick, handleModal }) { function CustomToolbar({ apiRef, handleModal }) {
const handleGoToPage1 = () => { const handleGoToPage1 = () => {
if (apiRef.current) { if (apiRef.current) {
apiRef.current.setPage(1); apiRef.current.setPage(1);
@ -27,9 +25,8 @@ function UserMaintance() {
const [userGroups, setUserGroups] = useState([]); const [userGroups, setUserGroups] = useState([]);
const [selectedUserGroup, setSelectedUserGroup] = useState(null); const [selectedUserGroup, setSelectedUserGroup] = useState(null);
const [isModalOpen, setIsModalOpen] = useState(false); const [isModalOpen, setIsModalOpen] = useState(false);
const [isUpdateModalOpen, setIsUpdateModalOpen] = useState(false);
const apiRef = useRef(null); const apiRef = useRef(null);
// eslint-disable-next-line
useEffect(() => { useEffect(() => {
const fetchData = async () => { const fetchData = async () => {
const token = localStorage.getItem("token"); const token = localStorage.getItem("token");
@ -165,7 +162,7 @@ function UserMaintance() {
className="three-dots" className="three-dots"
onClick={() => handleThreeDotsClick(row.usrGrp)} onClick={() => handleThreeDotsClick(row.usrGrp)}
> >
<FontAwesomeIcon icon={faEllipsisV} /> <BsThreeDotsVertical />
</div> </div>
{selectedUserGroup === row.usrGrp && ( {selectedUserGroup === row.usrGrp && (
<div className="popover"> <div className="popover">

View File

@ -1,8 +1,7 @@
import React, { useState, useEffect, useRef } from "react"; import React, { useState, useEffect, useRef } from "react";
import { Box, Button } from "@mui/material"; import { Box, Button } from "@mui/material";
import { DataGrid, GridToolbarContainer } from "@mui/x-data-grid"; import { DataGrid, GridToolbarContainer } from "@mui/x-data-grid";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { BsThreeDotsVertical } from "react-icons/bs";
import { faEllipsisV } from "@fortawesome/free-solid-svg-icons";
import Modal from "./Modal"; // Import your Modal component import Modal from "./Modal"; // Import your Modal component
import UpdateModal from "./UpdateModal"; // Import your UpdateModal component import UpdateModal from "./UpdateModal"; // Import your UpdateModal component
import "./UserMaintance.css"; import "./UserMaintance.css";
@ -109,7 +108,7 @@ function UserMaintance() {
className="three-dots" className="three-dots"
onClick={() => handleThreeDotsClick(row.userId)} onClick={() => handleThreeDotsClick(row.userId)}
> >
<FontAwesomeIcon icon={faEllipsisV} /> <BsThreeDotsVertical />
</div> </div>
{selectedUser === row.userId && ( {selectedUser === row.userId && (
<div className="popover"> <div className="popover">

View File

@ -121,3 +121,6 @@ button:hover {
.main-content h3 { .main-content h3 {
margin-top: 0; margin-top: 0;
} }

View File

@ -1,3 +1,4 @@
/* eslint-disable jsx-a11y/anchor-is-valid */
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import Sidebar from "./sidebar"; import Sidebar from "./sidebar";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
@ -6,19 +7,13 @@ import UserGroupMaintanceComponent from "./UserGroupMaintance/UserGroupMaintance
import MenuMaintanceComponent from "./MenuMaintance/MenuMaintance"; import MenuMaintanceComponent from "./MenuMaintance/MenuMaintance";
import MenuAccessControlComponent from "./MenuAccessControl/MenuAccessControl"; import MenuAccessControlComponent from "./MenuAccessControl/MenuAccessControl";
import SystemParametersComponent from "./SystemParameters/SystemParameters"; import SystemParametersComponent from "./SystemParameters/SystemParameters";
import AccessTypeComponent from "./AccessType/AccessType"; // import AccessTypeComponent from "./AccessType/AccessType";
import ApiRegistery from "./ApiRegistery/ApiRegistery"; import ApiRegistery from "./ApiRegistery/ApiRegistery";
import TokenRegistery from "./TokenRegistery/TokenRegistery"; import TokenRegistery from "./TokenRegistery/TokenRegistery";
import HomePage from "./HomePage"; import HomePage from "./HomePage";
import Setup from "./Setup.js"; import Setup from "./Setup.js";
import Report from "./Report"; import Report from "./Report";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FaCog, FaUsers, FaSignOutAlt, FaHome, FaChartBar } from "react-icons/fa";
import {
faCog,
faUsers,
faSignOutAlt,
faHome,
} from "@fortawesome/free-solid-svg-icons";
const Dashboard = () => { const Dashboard = () => {
const navigate = useNavigate(); const navigate = useNavigate();
@ -29,14 +24,16 @@ const Dashboard = () => {
useEffect(() => { useEffect(() => {
const fetchMenusData = async () => { const fetchMenusData = async () => {
const token = localStorage.getItem("token"); const token = localStorage.getItem("authtoken");
const apiUrl = `${process.env.REACT_APP_API_BASE_URL}/fndMenu/menuloadbyuser`; const apiUrl = `${process.env.REACT_APP_API_BASE_URL}/fndMenu/menuloadbyuser`;
console.log("Fetching menus from API:", apiUrl); console.log("Fetching menus from API:", apiUrl);
try { try {
const response = await fetch(apiUrl, { const response = await fetch(apiUrl, {
method: "GET",
headers: { headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`, Authorization: `Bearer ${token}`,
}, },
}); });
@ -92,36 +89,57 @@ const Dashboard = () => {
}; };
return ( return (
<div className="flex flex-col h-screen"> <div className="flex flex-col h-screen bg-gradient-to-br from-purple-50 via-white to-blue-50">
<div className="flex justify-between items-center bg-gray-800 text-white p-4"> {/* Top Navigation Bar */}
<h3 className="text-2xl">Dashboard</h3> <div className="flex justify-between items-center bg-gradient-to-r from-purple-600 to-indigo-600 text-white p-4 shadow-lg">
<nav className="flex space-x-4"> <div className="flex items-center space-x-4">
<a className="text-white" href="#" onClick={handleHomeClick}> <h3 className="text-2xl font-bold">Dashboard</h3>
<FontAwesomeIcon icon={faHome} /> <div className="h-6 w-px bg-white/30"></div>
</a> <nav className="flex space-x-6">
<a className="text-white" href="#" onClick={handleSetupClick}> <button
<FontAwesomeIcon icon={faCog} /> onClick={handleHomeClick}
</a> className="flex items-center space-x-2 hover:text-purple-200 transition-colors"
<a className="text-white" href="#" onClick={handleReportClick}> >
<FontAwesomeIcon icon={faUsers} /> <FaHome className="w-5 h-5" />
</a> <span>Home</span>
</nav> </button>
<button
onClick={handleSetupClick}
className="flex items-center space-x-2 hover:text-purple-200 transition-colors"
>
<FaCog className="w-5 h-5" />
<span>Setup</span>
</button>
<button
onClick={handleReportClick}
className="flex items-center space-x-2 hover:text-purple-200 transition-colors"
>
<FaChartBar className="w-5 h-5" />
<span>Reports</span>
</button>
</nav>
</div>
<button <button
className="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded" className="flex items-center space-x-2 bg-white/10 hover:bg-white/20 text-white font-semibold py-2 px-4 rounded-lg transition-all duration-200"
onClick={handleLogout} onClick={handleLogout}
> >
<FontAwesomeIcon icon={faSignOutAlt} /> Logout <FaSignOutAlt className="w-5 h-5" />
<span>Logout</span>
</button> </button>
</div> </div>
<div className="flex flex-1 overflow-hidden"> <div className="flex flex-1 overflow-hidden">
{/* Sidebar */}
<div <div
className={`bg-gray-700 text-white transition-all duration-300 ${ className={`bg-gradient-to-b from-purple-700 to-indigo-800 text-white transition-all duration-300 ${
sidebarCollapsed ? "w-16" : "w-64" sidebarCollapsed ? "w-16" : "w-64"
} min-w-16`} } min-w-16 shadow-xl`}
> >
<div className="flex justify-center p-2"> <div className="flex justify-end p-2">
<button onClick={handleSidebarToggle} className="text-white"> <button
onClick={handleSidebarToggle}
className="text-white/80 hover:text-white bg-white/10 hover:bg-white/20 p-2 rounded-lg transition-all duration-200"
>
{sidebarCollapsed ? ">>" : "<<"} {sidebarCollapsed ? ">>" : "<<"}
</button> </button>
</div> </div>
@ -133,41 +151,48 @@ const Dashboard = () => {
/> />
</div> </div>
<div className="flex-1 p-6 overflow-auto bg-gray-100"> {/* Main Content Area */}
{content === "Setup" ? ( <div className="flex-1 p-6 overflow-auto bg-gradient-to-br from-purple-50 via-white to-blue-50">
<Setup /> <div className="max-w-7xl mx-auto">
) : content === "Home" ? ( {content === "Setup" ? (
<HomePage /> <div className="bg-white rounded-xl shadow-lg p-6">
) : content === "Report" ? ( <Setup />
<Report /> </div>
) : selectedUserMaintance ? ( ) : content === "Home" ? (
<div> <div className="bg-white rounded-xl shadow-lg p-6">
<h3 className="text-2xl mb-4"> <HomePage />
{selectedUserMaintance.menuItemDesc} </div>
</h3> ) : content === "Report" ? (
{selectedUserMaintance.menuItemDesc === "User Maintance" ? ( <div className="bg-white rounded-xl shadow-lg p-6">
<UserMaintanceComponent /> <Report />
) : selectedUserMaintance.menuItemDesc === </div>
"User Group Maintance" ? ( ) : selectedUserMaintance ? (
<UserGroupMaintanceComponent /> <div className="bg-white rounded-xl shadow-lg p-6">
) : selectedUserMaintance.menuItemDesc === "Menu Maintance" ? ( <h3 className="text-2xl font-bold text-gray-800 mb-6 pb-2 border-b border-purple-100">
<MenuMaintanceComponent /> {selectedUserMaintance.menuItemDesc}
) : selectedUserMaintance.menuItemDesc === </h3>
"Menu Access Control" ? ( {selectedUserMaintance.menuItemDesc === "User Maintance" ? (
<MenuAccessControlComponent /> <UserMaintanceComponent />
) : selectedUserMaintance.menuItemDesc === "System Parameters" ? ( ) : selectedUserMaintance.menuItemDesc === "User Group Maintance" ? (
<SystemParametersComponent /> <UserGroupMaintanceComponent />
) : selectedUserMaintance.menuItemDesc === "Access Type" ? ( ) : selectedUserMaintance.menuItemDesc === "Menu Maintance" ? (
<AccessTypeComponent /> <MenuMaintanceComponent />
) : selectedUserMaintance.menuItemDesc === "Api Registery" ? ( ) : selectedUserMaintance.menuItemDesc === "Menu Access Control" ? (
<ApiRegistery /> <MenuAccessControlComponent />
) : selectedUserMaintance.menuItemDesc === "Token Registery" ? ( ) : selectedUserMaintance.menuItemDesc === "System Parameters" ? (
<TokenRegistery /> <SystemParametersComponent />
) : null} ) : selectedUserMaintance.menuItemDesc === "Api Registery" ? (
</div> <ApiRegistery />
) : ( ) : selectedUserMaintance.menuItemDesc === "Token Registery" ? (
<HomePage /> <TokenRegistery />
)} ) : null}
</div>
) : (
<div className="bg-white rounded-xl shadow-lg p-6">
<HomePage />
</div>
)}
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,78 +1,117 @@
import React, { useState } from 'react'; import React, { useEffect, useState } from "react";
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FiChevronRight, FiChevronDown, FiSettings } from "react-icons/fi";
import { import { Link } from "react-router-dom";
faAngleRight, faAngleDown, faUser, faUsers, faList, faLock, faCog, faFileAlt, faBook, faPen, faExchangeAlt, faUserShield, import { FaChevronRight as FaChevronRightIcon } from "react-icons/fa";
} from '@fortawesome/free-solid-svg-icons';
import './sidebar.css';
const Sidebar = ({ menus, handleMenuItemClick }) => { const Sidebar = ({ menus, handleMenuItemClick, collapsed }) => {
const [collapsed, setCollapsed] = useState(true); const [isOpen, setIsOpen] = useState(true);
const [openSubmenu, setOpenSubmenu] = useState(null);
const [menuItems, setMenuItems] = useState([]);
// Initialize collapsedSubmenus state useEffect(() => {
const [collapsedSubmenus, setCollapsedSubmenus] = useState( const fetchMenuItems = async () => {
menus.reduce((acc, menu) => { const token = localStorage.getItem("authToken");
acc[menu.moduleName] = true; try {
return acc; const res = await fetch(
}, {}) `${process.env.REACT_APP_API_BASE_URL}/fndMenu/menuloadbyuser`,
); {
headers: {
Authorization: `Bearer ${token}`,
},
}
);
const data = await res.json();
setMenuItems(data);
console.log("Fetched Menu Data:", data);
} catch (error) {
console.error("Error fetching menu items:", error);
}
};
const handleSubmenuClick = (menuName) => { fetchMenuItems();
setCollapsedSubmenus((prevCollapsedSubmenus) => ({ }, []);
...prevCollapsedSubmenus,
[menuName]: !prevCollapsedSubmenus[menuName], const handleToggle = () => setIsOpen(!isOpen);
})); const handleSubmenuToggle = (menu) => {
setOpenSubmenu(openSubmenu === menu ? null : menu);
}; };
return ( return (
<div className={`flex flex-col h-full ${collapsed ? 'w-16' : 'w-64'} bg-gray-800 text-white transition-all duration-300`}> <div
<div className="flex items-center justify-between p-4 bg-gray-900"> className={`flex flex-col h-screen bg-gray-100 text-black ${isOpen ? "w-64" : "w-20"
<button onClick={() => setCollapsed(!collapsed)}> } transition-all duration-300`}
<FontAwesomeIcon icon={collapsed ? faAngleRight : faAngleDown} /> >
<div className="flex items-center justify-between p-4">
{isOpen ? (
<span className="text-lg font-bold">Menu</span>
) : (
<FiSettings
className="text-2xl cursor-pointer"
onClick={handleToggle}
/>
)}
<button onClick={handleToggle}>
<svg
className="w-6 h-6"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M4 6h16M4 12h16m-7 6h7"
></path>
</svg>
</button> </button>
{!collapsed && <h2 className="ml-2">Cloudnsure</h2>}
</div> </div>
<ul className="flex-1 overflow-y-auto"> {isOpen && (
{menus.map((menu) => ( <nav className="flex flex-col mt-4">
<li key={menu.moduleName} className="group"> {menuItems.map((item) => (
<div <div key={item.id} className="relative">
onClick={() => handleSubmenuClick(menu.moduleName)} <button
className="flex items-center p-4 cursor-pointer hover:bg-gray-700" className="flex items-center p-4 hover:bg-gray-300 w-full text-left"
> onClick={() => handleSubmenuToggle(item.id)}
{/* Manually adding icons for specific menus */} >
{menu.moduleName === 'dashboard' && <FontAwesomeIcon icon={faPen} className="mr-2" />} {item.icon && <item.icon className="w-6 h-6 mr-2" />}
{menu.moduleName === 'sec3000' && <FontAwesomeIcon icon={faLock} className="mr-2" />} <span>{item.menuItemDesc}</span>
{menu.moduleName === 'Super Admin' && <FontAwesomeIcon icon={faUserShield} className="mr-2" />} {!collapsed && (
{menu.moduleName === 'Transaction' && <FontAwesomeIcon icon={faExchangeAlt} className="mr-2" />} <FaChevronRightIcon className="w-4 h-4 opacity-0 group-hover:opacity-100 transform group-hover:translate-x-1 transition-all duration-200" />
{/* Display menu name */} )}
<span className={`flex-1 ${collapsed ? 'hidden' : 'block'}`}>{menu.moduleName}</span> </button>
{/* Submenu toggle icon */} {/*
<FontAwesomeIcon icon={collapsedSubmenus[menu.moduleName] ? faAngleRight : faAngleDown} /> <Link to={`${subItem.menuItemDesc}`} ><span className="ml-4">{subItem.menuItemDesc}</span> </Link>
*/}
{item.subMenus && openSubmenu === item.id && (
<ul className="pl-8 bg-gray-200">
{item.subMenus.map((subItem) => (
<Link to={`/${subItem.menuItemDesc}`} style={{ textDecoration: 'none', }}>
<li
key={subItem.id}
className="flex items-center p-2 hover:bg-gray-300"
>
<span className="ml-4">{subItem.menuItemDesc}</span>
{subItem.subMenus && (
<div className="ml-auto">
{openSubmenu === subItem.id ? (
<FiChevronDown />
) : (
<FiChevronRight />
)}
</div>
)}
</li>
</Link>
))}
</ul>
)}
</div> </div>
{!collapsedSubmenus[menu.moduleName] && ( ))}
<ul className={`pl-8 ${collapsed ? 'hidden' : 'block'}`}> </nav>
{menu.subMenus.map((submenu, index) => ( )}
<li
key={index}
onClick={() => handleMenuItemClick(submenu)}
className="flex items-center p-2 cursor-pointer hover:bg-gray-600"
>
{/* Manually adding icons for specific submenus */}
{submenu.menuItemDesc === 'User Maintance' && <FontAwesomeIcon icon={faUser} className="mr-2" />}
{submenu.menuItemDesc === 'User Group Maintance' && <FontAwesomeIcon icon={faUsers} className="mr-2" />}
{submenu.menuItemDesc === 'Menu Maintance' && <FontAwesomeIcon icon={faList} className="mr-2" />}
{submenu.menuItemDesc === 'Menu Access Control' && <FontAwesomeIcon icon={faLock} className="mr-2" />}
{submenu.menuItemDesc === 'System Parameters' && <FontAwesomeIcon icon={faCog} className="mr-2" />}
{submenu.menuItemDesc === 'Access Type' && <FontAwesomeIcon icon={faFileAlt} className="mr-2" />}
{submenu.menuItemDesc === 'Document Sequence' && <FontAwesomeIcon icon={faBook} className="mr-2" />}
{/* Display submenu name */}
{submenu.menuItemDesc}
</li>
))}
</ul>
)}
</li>
))}
</ul>
</div> </div>
); );
}; };

View File

@ -1,18 +1,68 @@
import React, { useState } from 'react'; import React, { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom'; import { Eye, EyeOff, User, Lock, Mail, Camera, UserPlus, ArrowLeft, Check } from 'lucide-react';
import { AccountCircle, Visibility, VisibilityOff } from '@mui/icons-material';
import './Login.css'; // Import CSS file for custom styling // Mock navigation function for demo
const mockNavigate = (path) => {
console.log(`Navigating to: ${path}`);
alert(`Would navigate to: ${path}`);
};
const CreateAccountPage = () => { const CreateAccountPage = () => {
const [email, setEmail] = useState(''); const [email, setEmail] = useState('');
const [password, setPassword] = useState(''); const [password, setPassword] = useState('');
const [reEnterPassword, setReEnterPassword] = useState(''); const [reEnterPassword, setReEnterPassword] = useState('');
const [avatarImage, setAvatarImage] = useState(null); const [avatarImage, setAvatarImage] = useState(null);
const navigate = useNavigate(); const [showPassword, setShowPassword] = useState(false);
const [showReEnterPassword, setShowReEnterPassword] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [mounted, setMounted] = useState(false);
const [errorMessage, setErrorMessage] = useState('');
const [passwordStrength, setPasswordStrength] = useState(0);
const handleCreateAccount = (e) => { useEffect(() => {
setMounted(true);
}, []);
useEffect(() => {
// Calculate password strength
let strength = 0;
if (password.length >= 8) strength++;
if (/[A-Z]/.test(password)) strength++;
if (/[0-9]/.test(password)) strength++;
if (/[^A-Za-z0-9]/.test(password)) strength++;
setPasswordStrength(strength);
}, [password]);
const handleCreateAccount = async (e) => {
e.preventDefault(); e.preventDefault();
// Your create account logic here setErrorMessage('');
setIsLoading(true);
if (!email || !password || !reEnterPassword) {
setErrorMessage('All fields are required.');
setIsLoading(false);
return;
}
if (password !== reEnterPassword) {
setErrorMessage('Passwords do not match.');
setIsLoading(false);
return;
}
if (password.length < 8) {
setErrorMessage('Password must be at least 8 characters long.');
setIsLoading(false);
return;
}
// Simulate API call
setTimeout(() => {
console.log('Account created successfully!');
alert('Account created successfully!');
mockNavigate('/login');
setIsLoading(false);
}, 2000);
}; };
const handleAvatarChange = (e) => { const handleAvatarChange = (e) => {
@ -26,82 +76,256 @@ const CreateAccountPage = () => {
} }
}; };
const getPasswordStrengthColor = () => {
if (passwordStrength === 0) return 'bg-gray-300';
if (passwordStrength === 1) return 'bg-red-400';
if (passwordStrength === 2) return 'bg-yellow-400';
if (passwordStrength === 3) return 'bg-blue-400';
return 'bg-green-400';
};
const getPasswordStrengthText = () => {
if (passwordStrength === 0) return 'Enter password';
if (passwordStrength === 1) return 'Weak';
if (passwordStrength === 2) return 'Fair';
if (passwordStrength === 3) return 'Good';
return 'Strong';
};
return ( return (
<div className="relative min-h-screen flex items-center justify-center bg-gray-800"> <div className="min-h-screen bg-gradient-to-br from-purple-50 via-white to-purple-100 relative overflow-hidden">
<div className="absolute inset-0 flex items-center justify-center"> {/* Background Elements */}
<div className="text-center text-gray-200 text-9xl font-bold opacity-10"> <div className="absolute inset-0">
CLOUDNSURE {/* Subtle geometric shapes */}
</div> <div className="absolute top-20 left-20 w-32 h-32 bg-purple-200 rounded-full opacity-20 animate-pulse"></div>
<div className="absolute bottom-20 right-20 w-24 h-24 bg-purple-300 rounded-full opacity-20 animate-pulse" style={{ animationDelay: '1s' }}></div>
<div className="absolute top-1/2 right-10 w-16 h-16 bg-purple-400 rounded-full opacity-15 animate-pulse" style={{ animationDelay: '2s' }}></div>
{/* Grid pattern */}
<div className="absolute inset-0 opacity-5" style={{
backgroundImage: `url("data:image/svg+xml,%3Csvg width='40' height='40' viewBox='0 0 40 40' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%236B46C1' fill-opacity='0.4'%3E%3Cpath d='M20 20h20v20H20V20zm-20 0h20v20H0V20z'/%3E%3C/g%3E%3C/svg%3E")`
}}></div>
</div> </div>
<div className="relative w-full max-w-md bg-white shadow-md rounded-lg p-6 z-10">
<div className="flex items-center justify-center mb-6"> {/* Main Content */}
<AccountCircle className="text-7xl text-gray-700" /> <div className="relative z-10 min-h-screen flex items-center justify-center p-4">
{/* Background Logo */}
<div className="absolute inset-0 flex items-center justify-center pointer-events-none">
<div className={`text-center transition-all duration-2000 ${mounted ? 'opacity-5 scale-100' : 'opacity-0 scale-95'}`}>
</div>
</div> </div>
<h2 className="text-2xl font-semibold text-center text-gray-800 mb-4">Create Account</h2>
<form onSubmit={handleCreateAccount} className="space-y-4"> {/* Create Account Card */}
<div className="flex items-center justify-center"> <div className={`relative w-full max-w-lg transition-all duration-1000 ${mounted ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}>
<input {/* Card Background */}
accept="image/*" <div className="bg-white rounded-3xl shadow-2xl border border-purple-100 overflow-hidden">
id="avatar-input" {/* Header Section */}
type="file" <div className="bg-gradient-to-r from-purple-600 to-purple-700 p-8 text-center relative">
className="hidden" <div className="absolute top-4 left-4">
onChange={handleAvatarChange} <button
/> onClick={() => mockNavigate('/login')}
<label htmlFor="avatar-input" className="cursor-pointer"> className="p-2 text-white hover:bg-white hover:bg-opacity-20 rounded-xl transition-all duration-200"
<img >
alt="Avatar" <ArrowLeft className="w-5 h-5" />
src={avatarImage || 'https://via.placeholder.com/120'} </button>
className="w-28 h-28 rounded-full mb-4" </div>
/>
</label> <div className="space-y-4">
<div className="inline-flex items-center justify-center w-16 h-16 bg-white bg-opacity-20 rounded-2xl">
<UserPlus className="w-8 h-8 text-white" />
</div>
<div>
<h1 className="text-3xl font-bold text-white mb-2">Create Account</h1>
<p className="text-purple-100">Join our community today</p>
</div>
</div>
</div>
{/* Form Section */}
<div className="p-8 space-y-6">
{/* Error Message */}
{errorMessage && (
<div className="bg-red-50 border border-red-200 rounded-xl p-4 text-red-700 text-sm">
{errorMessage}
</div>
)}
{/* Avatar Upload */}
<div className="text-center">
<div className="relative inline-block">
<input
accept="image/*"
id="avatar-input"
type="file"
className="hidden"
onChange={handleAvatarChange}
/>
<label htmlFor="avatar-input" className="cursor-pointer group">
<div className="relative">
<img
alt="Avatar"
src={avatarImage || 'https://via.placeholder.com/120/E5E7EB/6B7280?text=Photo'}
className="w-28 h-28 rounded-full border-4 border-purple-100 group-hover:border-purple-300 transition-all duration-200 object-cover"
/>
<div className="absolute inset-0 bg-black bg-opacity-0 group-hover:bg-opacity-30 rounded-full transition-all duration-200 flex items-center justify-center">
<Camera className="w-6 h-6 text-white opacity-0 group-hover:opacity-100 transition-opacity duration-200" />
</div>
</div>
</label>
</div>
<p className="text-sm text-gray-500 mt-2">Click to upload your photo</p>
</div>
{/* Form Fields */}
<div className="space-y-5">
{/* Email Field */}
<div className="space-y-2">
<label className="text-sm font-medium text-gray-700">Email Address</label>
<div className="relative group">
<div className="absolute left-4 top-1/2 transform -translate-y-1/2">
<Mail className="w-5 h-5 text-gray-400 group-focus-within:text-purple-500 transition-colors" />
</div>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="w-full pl-12 pr-4 py-4 bg-gray-50 border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent transition-all duration-200"
placeholder="Enter your email"
/>
</div>
</div>
{/* Password Field */}
<div className="space-y-2">
<label className="text-sm font-medium text-gray-700">Password</label>
<div className="relative group">
<div className="absolute left-4 top-1/2 transform -translate-y-1/2">
<Lock className="w-5 h-5 text-gray-400 group-focus-within:text-purple-500 transition-colors" />
</div>
<input
type={showPassword ? 'text' : 'password'}
value={password}
onChange={(e) => setPassword(e.target.value)}
className="w-full pl-12 pr-12 py-4 bg-gray-50 border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent transition-all duration-200"
placeholder="Create a strong password"
/>
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="absolute right-4 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-600 transition-colors"
>
{showPassword ? <EyeOff className="w-5 h-5" /> : <Eye className="w-5 h-5" />}
</button>
</div>
{/* Password Strength Indicator */}
{password && (
<div className="space-y-2">
<div className="flex items-center space-x-2">
<div className="flex-1 bg-gray-200 rounded-full h-2">
<div
className={`h-2 rounded-full transition-all duration-300 ${getPasswordStrengthColor()}`}
style={{ width: `${(passwordStrength / 4) * 100}%` }}
></div>
</div>
<span className="text-xs font-medium text-gray-600">
{getPasswordStrengthText()}
</span>
</div>
<div className="text-xs text-gray-500 space-y-1">
<div className="flex items-center space-x-2">
<div className={`w-2 h-2 rounded-full ${password.length >= 8 ? 'bg-green-400' : 'bg-gray-300'}`}></div>
<span>At least 8 characters</span>
</div>
<div className="flex items-center space-x-2">
<div className={`w-2 h-2 rounded-full ${/[A-Z]/.test(password) ? 'bg-green-400' : 'bg-gray-300'}`}></div>
<span>One uppercase letter</span>
</div>
<div className="flex items-center space-x-2">
<div className={`w-2 h-2 rounded-full ${/[0-9]/.test(password) ? 'bg-green-400' : 'bg-gray-300'}`}></div>
<span>One number</span>
</div>
</div>
</div>
)}
</div>
{/* Re-enter Password Field */}
<div className="space-y-2">
<label className="text-sm font-medium text-gray-700">Confirm Password</label>
<div className="relative group">
<div className="absolute left-4 top-1/2 transform -translate-y-1/2">
<Lock className="w-5 h-5 text-gray-400 group-focus-within:text-purple-500 transition-colors" />
</div>
<input
type={showReEnterPassword ? 'text' : 'password'}
value={reEnterPassword}
onChange={(e) => setReEnterPassword(e.target.value)}
className={`w-full pl-12 pr-12 py-4 bg-gray-50 border rounded-xl focus:outline-none focus:ring-2 focus:border-transparent transition-all duration-200 ${
reEnterPassword && password !== reEnterPassword
? 'border-red-300 focus:ring-red-500'
: reEnterPassword && password === reEnterPassword
? 'border-green-300 focus:ring-green-500'
: 'border-gray-200 focus:ring-purple-500'
}`}
placeholder="Confirm your password"
/>
<button
type="button"
onClick={() => setShowReEnterPassword(!showReEnterPassword)}
className="absolute right-4 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-600 transition-colors"
>
{showReEnterPassword ? <EyeOff className="w-5 h-5" /> : <Eye className="w-5 h-5" />}
</button>
{reEnterPassword && password === reEnterPassword && (
<div className="absolute right-12 top-1/2 transform -translate-y-1/2">
<Check className="w-5 h-5 text-green-500" />
</div>
)}
</div>
{reEnterPassword && password !== reEnterPassword && (
<p className="text-xs text-red-500">Passwords do not match</p>
)}
</div>
</div>
{/* Create Account Button */}
<button
type="button"
onClick={handleCreateAccount}
disabled={isLoading || !email || !password || !reEnterPassword || password !== reEnterPassword}
className="w-full py-4 px-6 bg-gradient-to-r from-purple-600 to-purple-700 text-white font-semibold rounded-xl shadow-lg hover:shadow-xl transform hover:scale-105 transition-all duration-200 disabled:opacity-50 disabled:transform-none disabled:cursor-not-allowed"
>
<div className="flex items-center justify-center space-x-2">
{isLoading ? (
<>
<div className="w-5 h-5 border-2 border-white border-t-transparent rounded-full animate-spin"></div>
<span>Creating Account...</span>
</>
) : (
<>
<UserPlus className="w-5 h-5" />
<span>Create Account</span>
</>
)}
</div>
</button>
{/* Login Link */}
<div className="text-center pt-4 border-t border-gray-100">
<p className="text-gray-600 mb-3">Already have an account?</p>
<button
type="button"
onClick={() => mockNavigate('/login')}
className="inline-flex items-center space-x-2 text-purple-600 hover:text-purple-700 font-semibold transition-colors"
>
<User className="w-4 h-4" />
<span>Sign In</span>
</button>
</div>
</div>
</div> </div>
<div>
<label className="block text-gray-600">Email</label>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="w-full px-4 py-2 mt-2 border rounded-md focus:outline-none focus:ring focus:ring-opacity-50"
/>
</div>
<div>
<label className="block text-gray-600">Password</label>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
className="w-full px-4 py-2 mt-2 border rounded-md focus:outline-none focus:ring focus:ring-opacity-50"
/>
</div>
<div>
<label className="block text-gray-600">Re-enter Password</label>
<input
type="password"
value={reEnterPassword}
onChange={(e) => setReEnterPassword(e.target.value)}
className="w-full px-4 py-2 mt-2 border rounded-md focus:outline-none focus:ring focus:ring-opacity-50"
/>
</div>
<div className="text-center">
<button
type="submit"
className="w-full py-2 px-4 bg-blue-600 text-white rounded-md hover:bg-blue-700 focus:outline-none focus:ring focus:ring-blue-300 transition duration-300 ease-in-out"
>
Create Account
</button>
</div>
</form>
<div className="mt-4 text-center">
<p className="text-gray-600">
Already have an account?{' '}
<button
type="button"
onClick={() => navigate('/login')}
className="text-blue-600 hover:underline focus:outline-none"
>
Log in
</button>
</p>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,96 +1,191 @@
import React, { useState } from 'react'; import React, { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom'; import { Mail, ArrowLeft, Shield, CheckCircle, AlertCircle, Sparkles } from 'lucide-react';
import { AccountCircle } from '@mui/icons-material';
import './Login.css'; // Import CSS file for custom styling
const API_BASE_URL = process.env.REACT_APP_API_BASE_URL;
const API_FORGOT_PASSWORD = `${API_BASE_URL}/backend/api/resources/forgotpassword`;
const ForgotPasswordPage = () => { const ForgotPasswordPage = () => {
const [email, setEmail] = useState(''); const [email, setEmail] = useState('');
const [message, setMessage] = useState(''); const [message, setMessage] = useState('');
const [messageType, setMessageType] = useState(''); // State to manage message type for styling const [messageType, setMessageType] = useState('');
const navigate = useNavigate(); const [isLoading, setIsLoading] = useState(false);
const [mounted, setMounted] = useState(false);
const [particles, setParticles] = useState([]);
useEffect(() => {
setMounted(true);
const newParticles = Array.from({ length: 35 }, (_, i) => ({
id: i,
x: Math.random() * 100,
y: Math.random() * 100,
size: Math.random() * 3 + 1,
duration: Math.random() * 4 + 3,
delay: Math.random() * 2,
}));
setParticles(newParticles);
}, []);
const handleSubmit = async (e) => { const handleSubmit = async (e) => {
e.preventDefault(); e.preventDefault();
setIsLoading(true);
setMessage('');
setMessageType('');
try { try {
const response = await fetch(API_FORGOT_PASSWORD, { await new Promise(resolve => setTimeout(resolve, 2000));
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ email }),
});
if (!response.ok) { if (email.includes('@')) {
const errorText = await response.text(); setMessage('Reset password email sent successfully. Please check your email.');
setMessage(`Reset password failed: ${errorText}`); setMessageType('success');
setEmail('');
} else {
setMessage('Please enter a valid email address.');
setMessageType('error'); setMessageType('error');
return;
} }
setMessage('Reset password email sent successfully. Please check your email.');
setMessageType('success');
} catch (error) { } catch (error) {
setMessage(`Error during reset password: ${error.message}`); setMessage(`Error during reset password: ${error.message}`);
setMessageType('error'); setMessageType('error');
console.error('Error during reset password:', error); } finally {
setIsLoading(false);
} }
}; };
const handleBackToLogin = () => {
alert('Back to login functionality would be implemented here');
};
return ( return (
<div className="relative min-h-screen flex items-center justify-center bg-gray-800"> <div className="min-h-screen relative overflow-hidden bg-gradient-to-br from-gray-50 via-white to-purple-50">
<div className="absolute inset-0 flex items-center justify-center"> <div className="absolute inset-0">
<div className="text-center text-gray-200 text-9xl font-bold opacity-10"> <div className="absolute top-1/4 left-1/4 w-80 h-80 bg-purple-200 rounded-full mix-blend-multiply filter blur-xl opacity-40 animate-pulse"></div>
CLOUDNSURE <div className="absolute top-1/3 right-1/4 w-96 h-96 bg-purple-300 rounded-full mix-blend-multiply filter blur-xl opacity-30 animate-pulse" style={{ animationDelay: '2s' }}></div>
</div> <div className="absolute bottom-1/4 left-1/3 w-72 h-72 bg-indigo-200 rounded-full mix-blend-multiply filter blur-xl opacity-35 animate-pulse" style={{ animationDelay: '4s' }}></div>
{particles.map((particle) => (
<div
key={particle.id}
className="absolute bg-purple-400 rounded-full opacity-20 animate-bounce"
style={{
left: `${particle.x}%`,
top: `${particle.y}%`,
width: `${particle.size}px`,
height: `${particle.size}px`,
animationDuration: `${particle.duration}s`,
animationDelay: `${particle.delay}s`,
}}
/>
))}
<div className="absolute inset-0 opacity-20" style={{
backgroundImage: `url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%239333ea' fill-opacity='0.03'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E")`
}}></div>
</div> </div>
<div className="relative w-full max-w-md bg-white shadow-md rounded-lg p-6 z-10">
<div className="flex items-center justify-center mb-6"> <div className="relative z-10 min-h-screen flex items-center justify-center p-4">
<AccountCircle className="text-7xl text-gray-700" /> <div className="absolute inset-0 flex items-center justify-center pointer-events-none">
<div className={`text-center transition-all duration-2000 ${mounted ? 'opacity-5 scale-100' : 'opacity-0 scale-95'}`}>
<div className="flex justify-center mt-4 space-x-2">
{[...Array(5)].map((_, i) => (
<Sparkles key={i} className="w-8 h-8 text-purple-300 opacity-30 animate-pulse" style={{ animationDelay: `${i * 0.3}s` }} />
))}
</div>
</div>
</div> </div>
<h2 className="text-2xl font-semibold text-center text-gray-800 mb-4">Forgot Password</h2>
<p className="text-center text-gray-600 mb-4"> <div className={`relative w-full max-w-md transition-all duration-1000 ${mounted ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}>
Enter your email address and we'll send you a link to reset your password. <div className="relative bg-white rounded-3xl shadow-2xl border border-purple-100 overflow-hidden">
</p> <div className="absolute inset-0 bg-gradient-to-r from-purple-500 via-purple-600 to-indigo-600 opacity-5 rounded-3xl"></div>
<form onSubmit={handleSubmit} className="space-y-4">
<div> <div className="relative p-8 space-y-6">
<label className="block text-gray-600">Email</label> <button
<input onClick={handleBackToLogin}
type="email" className="group absolute top-6 left-6 p-2 text-gray-400 hover:text-purple-600 transition-colors rounded-full hover:bg-purple-50"
value={email} >
onChange={(e) => setEmail(e.target.value)} <ArrowLeft className="w-5 h-5 group-hover:-translate-x-1 transition-transform" />
className="w-full px-4 py-2 mt-2 border rounded-md focus:outline-none focus:ring focus:ring-opacity-50" </button>
required
autoFocus <div className="text-center space-y-4 pt-8">
/> <div className="inline-flex items-center justify-center w-20 h-20 bg-gradient-to-br from-purple-500 to-indigo-600 rounded-2xl shadow-lg transform hover:rotate-12 transition-transform duration-300">
<Shield className="w-10 h-10 text-white" />
</div>
<div>
<h1 className="text-3xl font-bold text-gray-800 mb-2">Forgot Password?</h1>
<p className="text-gray-600 leading-relaxed">Don't worry! Enter your email address and we'll send you a secure link to reset your password.</p>
</div>
</div>
{message && (
<div className={`rounded-xl p-4 text-sm animate-pulse flex items-center space-x-3 ${
messageType === 'error'
? 'bg-red-50 border border-red-200 text-red-700'
: 'bg-green-50 border border-green-200 text-green-700'
}`}>
{messageType === 'error' ? (
<AlertCircle className="w-5 h-5 flex-shrink-0" />
) : (
<CheckCircle className="w-5 h-5 flex-shrink-0" />
)}
<span>{message}</span>
</div>
)}
<div className="space-y-6">
<div className="space-y-2">
<label className="text-sm font-medium text-gray-700">Email Address</label>
<div className="relative group">
<div className="absolute inset-0 bg-gradient-to-r from-purple-400 to-indigo-500 rounded-xl blur opacity-20 group-hover:opacity-30 transition-opacity"></div>
<div className="relative bg-gray-50 border-2 border-gray-200 rounded-xl overflow-hidden focus-within:border-purple-500 focus-within:bg-white transition-all">
<div className="absolute left-4 top-1/2 transform -translate-y-1/2">
<Mail className="w-5 h-5 text-gray-400" />
</div>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="w-full pl-12 pr-4 py-4 bg-transparent text-gray-800 placeholder-gray-500 focus:outline-none"
placeholder="Enter your email address"
required
autoFocus
/>
</div>
</div>
</div>
<button
type="submit"
onClick={handleSubmit}
disabled={isLoading}
className="group relative w-full py-4 px-6 bg-gradient-to-r from-purple-600 to-indigo-600 text-white font-semibold rounded-xl shadow-lg hover:shadow-xl hover:from-purple-700 hover:to-indigo-700 transform hover:scale-105 transition-all duration-200 disabled:opacity-50 disabled:transform-none overflow-hidden"
>
<div className="absolute inset-0 bg-gradient-to-r from-purple-500 to-indigo-500 opacity-0 group-hover:opacity-100 transition-opacity"></div>
<div className="relative flex items-center justify-center space-x-2">
{isLoading ? (
<>
<div className="w-5 h-5 border-2 border-white border-t-transparent rounded-full animate-spin"></div>
<span>Sending Reset Link...</span>
</>
) : (
<>
<Mail className="w-5 h-5" />
<span>Send Reset Link</span>
</>
)}
</div>
</button>
</div>
<div className="text-center pt-4 border-t border-gray-200">
<p className="text-gray-600 mb-3">Remember your password?</p>
<button
type="button"
onClick={handleBackToLogin}
className="group inline-flex items-center space-x-2 text-transparent bg-clip-text bg-gradient-to-r from-purple-600 to-indigo-600 font-semibold hover:from-purple-700 hover:to-indigo-700 transition-all"
>
<ArrowLeft className="w-4 h-4 text-purple-600 group-hover:text-purple-700 group-hover:-translate-x-1 transition-all" />
<span>Back to Sign In</span>
</button>
</div>
</div>
</div> </div>
<div className="text-center"> </div>
<button
type="submit"
className="w-full py-2 px-4 bg-blue-600 text-white rounded-md hover:bg-blue-700 focus:outline-none focus:ring focus:ring-blue-300 transition duration-300 ease-in-out"
>
Reset Password
</button>
</div>
</form>
{message && (
<div className={`mt-4 text-center ${messageType === 'error' ? 'text-red-500' : 'text-green-500'}`}>
{message}
</div>
)}
<p className="mt-4 text-center text-gray-600">
Remember your password?{' '}
<button
type="button"
onClick={() => navigate('/login')}
className="text-blue-600 hover:underline focus:outline-none"
>
Log in
</button>
</p>
</div> </div>
</div> </div>
); );

View File

@ -1,61 +1,55 @@
import React, { useState } from 'react'; import React, { useState, useEffect } from 'react';
import { Eye, EyeOff, User, Lock, Mail, Sparkles, Shield, ArrowRight } from 'lucide-react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { AccountCircle, Visibility, VisibilityOff } from '@mui/icons-material';
import './Login.css'; // Import CSS file for custom styling
const API_BASE_URL = process.env.REACT_APP_API_BASE_URL;
const API_TOKEN_SESSION = `${API_BASE_URL}/token/session`;
const LoginPage = () => { const LoginPage = () => {
const [email, setEmail] = useState(''); const navigate = useNavigate();
const [username, setUsername] = useState('');
const [password, setPassword] = useState(''); const [password, setPassword] = useState('');
const [showPassword, setShowPassword] = useState(false); const [showPassword, setShowPassword] = useState(false);
const [rememberMe, setRememberMe] = useState(false); const [rememberMe, setRememberMe] = useState(false);
const [errorMessage, setErrorMessage] = useState(''); const [errorMessage, setErrorMessage] = useState('');
const navigate = useNavigate(); const [isLoading, setIsLoading] = useState(false);
const [mounted, setMounted] = useState(false);
const [particles, setParticles] = useState([]);
useEffect(() => {
setMounted(true);
const newParticles = Array.from({ length: 40 }, (_, i) => ({
id: i,
x: Math.random() * 100,
y: Math.random() * 100,
size: Math.random() * 3 + 1,
duration: Math.random() * 4 + 3,
delay: Math.random() * 2,
}));
setParticles(newParticles);
}, []);
const handleLogin = async (e) => { const handleLogin = async (e) => {
e.preventDefault(); e.preventDefault();
setErrorMessage(''); setErrorMessage('');
setIsLoading(true);
if (!email || !password) { if (!username || !password) {
setErrorMessage('Email and password are required.'); setErrorMessage('Username and password are required.');
setIsLoading(false);
return; return;
} }
try { const storedCredentials = JSON.parse(localStorage.getItem('userCredentials') || '{}');
const response = await fetch(API_TOKEN_SESSION, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ email, password }),
});
if (!response.ok) { if ((username === 'sysadmin' && password === 'test3') ||
const errorText = await response.text(); (storedCredentials[username] && storedCredentials[username] === password)) {
console.error('Login failed:', errorText); localStorage.setItem('authToken', 'dummy-token');
setErrorMessage('Login failed. Please check your credentials.'); localStorage.setItem('currentUser', username);
return; navigate('/Dashboard');
} } else {
setErrorMessage('Invalid username or password. Please use sysadmin/test3 or your registered credentials.');
const data = await response.json();
console.log('Login response:', data);
if (data.operationStatus !== 'SUCCESS') {
console.error('Login failed:', data.operationMessage);
setErrorMessage(data.operationMessage || 'Login failed. Please try again.');
return;
}
localStorage.setItem('authToken', data.item.token);
localStorage.setItem('user', JSON.stringify(data.item));
console.log('Token stored in local storage:', data.item.token);
navigate('/dashboard');
} catch (error) {
console.error('Error during login:', error);
setErrorMessage('An error occurred during login. Please try again later.');
} }
setIsLoading(false);
}; };
const handleForgotPassword = () => { const handleForgotPassword = () => {
@ -67,86 +61,174 @@ const LoginPage = () => {
}; };
return ( return (
<div className="relative min-h-screen flex items-center justify-center bg-gray-700"> <div className="min-h-screen relative overflow-hidden bg-gradient-to-br from-gray-50 via-white to-purple-50">
<div className="absolute inset-0 flex items-center justify-center"> <div className="absolute inset-0">
<div className="text-center text-gray-100 text-9xl font-bold opacity-75"> <div className="absolute top-1/4 left-1/4 w-80 h-80 bg-purple-200 rounded-full mix-blend-multiply filter blur-xl opacity-40 animate-pulse"></div>
CLOUDNSURE <div className="absolute top-1/3 right-1/4 w-96 h-96 bg-purple-300 rounded-full mix-blend-multiply filter blur-xl opacity-30 animate-pulse" style={{ animationDelay: '2s' }}></div>
</div> <div className="absolute bottom-1/4 left-1/3 w-72 h-72 bg-indigo-200 rounded-full mix-blend-multiply filter blur-xl opacity-35 animate-pulse" style={{ animationDelay: '4s' }}></div>
{particles.map((particle) => (
<div
key={particle.id}
className="absolute bg-purple-400 rounded-full opacity-20 animate-bounce"
style={{
left: `${particle.x}%`,
top: `${particle.y}%`,
width: `${particle.size}px`,
height: `${particle.size}px`,
animationDuration: `${particle.duration}s`,
animationDelay: `${particle.delay}s`,
}}
/>
))}
<div className="absolute inset-0 opacity-20" style={{
backgroundImage: `url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%239333ea' fill-opacity='0.03'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E")`
}}></div>
</div> </div>
<div className="relative w-full max-w-md bg-white shadow-md rounded-lg p-6 z-10 ">
<div className="flex items-center justify-center mb-6"> <div className="relative z-10 min-h-screen flex items-center justify-center p-4">
<AccountCircle className="text-7xl text-gray-700" /> <div className="absolute inset-0 flex items-center justify-center pointer-events-none">
</div> <div className={`text-center transition-all duration-2000 ${mounted ? 'opacity-5 scale-100' : 'opacity-0 scale-95'}`}>
<h2 className="text-2xl font-semibold text-center text-gray-800 mb-4 ">Log in</h2> <div className="flex justify-center mt-4 space-x-2">
{errorMessage && <div className="mb-4 text-red-600">{errorMessage}</div>} {[...Array(5)].map((_, i) => (
<form onSubmit={handleLogin} className="space-y-4"> <Sparkles key={i} className="w-8 h-8 text-purple-300 opacity-30 animate-pulse" style={{ animationDelay: `${i * 0.3}s` }} />
<div> ))}
<label className="block text-gray-600">Email</label> </div>
<input
type="text"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="w-full px-4 py-2 mt-2 border rounded-md focus:outline-none focus:ring focus:ring-opacity-50"
/>
</div> </div>
<div> </div>
<label className="block text-gray-600">Password</label>
<div className="relative"> <div className={`relative w-full max-w-md transition-all duration-1000 ${mounted ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}>
<input <div className="relative bg-white rounded-3xl shadow-2xl border border-purple-100 overflow-hidden">
type={showPassword ? 'text' : 'password'} <div className="absolute inset-0 bg-gradient-to-r from-purple-500 via-purple-600 to-indigo-600 opacity-5 rounded-3xl"></div>
value={password}
onChange={(e) => setPassword(e.target.value)} <div className="relative p-8 space-y-6">
className="w-full px-4 py-2 mt-2 border rounded-md focus:outline-none focus:ring focus:ring-opacity-50" <div className="text-center space-y-4">
/> <div className="inline-flex items-center justify-center w-20 h-20 bg-gradient-to-br from-purple-500 to-indigo-600 rounded-2xl shadow-lg transform hover:rotate-12 transition-transform duration-300">
<div className="absolute inset-y-0 right-0 pr-3 flex items-center"> <Shield className="w-10 h-10 text-white" />
</div>
<div>
<h1 className="text-3xl font-bold text-gray-800 mb-2">Welcome Back</h1>
<p className="text-gray-600">Sign in to your account</p>
</div>
</div>
{errorMessage && (
<div className="bg-red-50 border border-red-200 rounded-xl p-4 text-red-700 text-sm animate-pulse">
{errorMessage}
</div>
)}
<div className="space-y-6">
<div className="space-y-2">
<label className="text-sm font-medium text-gray-700">Username</label>
<div className="relative group">
<div className="absolute inset-0 bg-gradient-to-r from-purple-400 to-indigo-500 rounded-xl blur opacity-20 group-hover:opacity-30 transition-opacity"></div>
<div className="relative bg-gray-50 border-2 border-gray-200 rounded-xl overflow-hidden focus-within:border-purple-500 focus-within:bg-white transition-all">
<div className="absolute left-4 top-1/2 transform -translate-y-1/2">
<User className="w-5 h-5 text-gray-400" />
</div>
<input
type="text"
value={username}
onChange={(e) => setUsername(e.target.value)}
className="w-full pl-12 pr-4 py-4 bg-transparent text-gray-800 placeholder-gray-500 focus:outline-none"
placeholder="Enter your username"
/>
</div>
</div>
</div>
<div className="space-y-2">
<label className="text-sm font-medium text-gray-700">Password</label>
<div className="relative group">
<div className="absolute inset-0 bg-gradient-to-r from-purple-400 to-indigo-500 rounded-xl blur opacity-20 group-hover:opacity-30 transition-opacity"></div>
<div className="relative bg-gray-50 border-2 border-gray-200 rounded-xl overflow-hidden focus-within:border-purple-500 focus-within:bg-white transition-all">
<div className="absolute left-4 top-1/2 transform -translate-y-1/2">
<Lock className="w-5 h-5 text-gray-400" />
</div>
<input
type={showPassword ? 'text' : 'password'}
value={password}
onChange={(e) => setPassword(e.target.value)}
className="w-full pl-12 pr-12 py-4 bg-transparent text-gray-800 placeholder-gray-500 focus:outline-none"
placeholder="Enter your password"
/>
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="absolute right-4 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-purple-600 transition-colors"
>
{showPassword ? <EyeOff className="w-5 h-5" /> : <Eye className="w-5 h-5" />}
</button>
</div>
</div>
</div>
<div className="flex items-center justify-between">
<label className="flex items-center space-x-3 cursor-pointer group">
<div className="relative">
<input
type="checkbox"
checked={rememberMe}
onChange={(e) => setRememberMe(e.target.checked)}
className="sr-only"
/>
<div className={`w-5 h-5 rounded border-2 transition-all ${rememberMe ? 'bg-gradient-to-r from-purple-500 to-indigo-600 border-purple-500' : 'border-gray-300 group-hover:border-purple-400'}`}>
{rememberMe && (
<svg className="w-3 h-3 text-white absolute top-0.5 left-0.5" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
</svg>
)}
</div>
</div>
<span className="text-sm text-gray-600 group-hover:text-purple-700 transition-colors">Remember me</span>
</label>
<button
type="button"
onClick={handleForgotPassword}
className="text-sm text-purple-600 hover:text-purple-700 transition-colors font-medium"
>
Forgot password?
</button>
</div>
<button <button
type="button" type="button"
onClick={() => setShowPassword(!showPassword)} onClick={handleLogin}
className="" disabled={isLoading}
className="group relative w-full py-4 px-6 bg-gradient-to-r from-purple-600 to-indigo-600 text-white font-semibold rounded-xl shadow-lg hover:shadow-xl hover:from-purple-700 hover:to-indigo-700 transform hover:scale-105 transition-all duration-200 disabled:opacity-50 disabled:transform-none overflow-hidden"
> >
{showPassword ? <VisibilityOff /> : <Visibility />} <div className="absolute inset-0 bg-gradient-to-r from-purple-500 to-indigo-500 opacity-0 group-hover:opacity-100 transition-opacity"></div>
<div className="relative flex items-center justify-center space-x-2">
{isLoading ? (
<>
<div className="w-5 h-5 border-2 border-white border-t-transparent rounded-full animate-spin"></div>
<span>Signing in...</span>
</>
) : (
<>
<span>Sign In</span>
<ArrowRight className="w-5 h-5 group-hover:translate-x-1 transition-transform" />
</>
)}
</div>
</button>
</div>
<div className="text-center pt-4 border-t border-gray-200">
<p className="text-gray-600 mb-3">Don't have an account?</p>
<button
type="button"
onClick={handleCreateAccount}
className="group inline-flex items-center space-x-2 text-transparent bg-clip-text bg-gradient-to-r from-purple-600 to-indigo-600 font-semibold hover:from-purple-700 hover:to-indigo-700 transition-all"
>
<User className="w-4 h-4 text-purple-600 group-hover:text-purple-700" />
<span>Create New Account</span>
</button> </button>
</div> </div>
</div> </div>
</div> </div>
<div className="flex items-center justify-between">
<label className="flex items-center">
<input
type="checkbox"
checked={rememberMe}
onChange={(e) => setRememberMe(e.target.checked)}
className="form-checkbox"
/>
<span className="ml-2 text-gray-600">Remember Me</span>
</label>
<button
type="button"
onClick={handleForgotPassword}
className="text-sm text-blue-600 hover:underline focus:outline-none"
>
Forgot password?
</button>
</div>
<div className="text-center">
<button
type="submit"
className="w-full py-2 px-4 bg-blue-600 text-white rounded-md hover:bg-blue-700 focus:outline-none focus:ring focus:ring-blue-300"
>
Login
</button>
</div>
</form>
<div className="mt-4 text-center">
<p className="text-gray-600">
Don't have an account?{' '}
<button
type="button"
onClick={handleCreateAccount}
className="text-blue-600 hover:underline focus:outline-none"
>
Create Account
</button>
</p>
</div> </div>
</div> </div>
</div> </div>

View File

@ -17,3 +17,5 @@ code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace; monospace;
} }

View File

@ -1,7 +1,6 @@
// index.js // index.js
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import { createRoot } from 'react-dom/client';
import { BrowserRouter as Router } from 'react-router-dom';
import { ThemeProvider, createTheme } from '@mui/material/styles'; import { ThemeProvider, createTheme } from '@mui/material/styles';
import App from './App'; import App from './App';
import './index.css'; import './index.css';
@ -11,13 +10,13 @@ const theme = createTheme({
// your theme configuration // your theme configuration
}); });
ReactDOM.render( const container = document.getElementById('root');
const root = createRoot(container);
root.render(
<ErrorBoundary> <ErrorBoundary>
<ThemeProvider theme={theme}> <ThemeProvider theme={theme}>
<Router> <App />
<App />
</Router>
</ThemeProvider> </ThemeProvider>
</ErrorBoundary>, </ErrorBoundary>
document.getElementById('root')
); );

23
src/utils/tokenService.js Normal file
View File

@ -0,0 +1,23 @@
const TOKEN_KEY = 'authToken';
export const setToken = (token) => {
localStorage.setItem(TOKEN_KEY, token);
};
export const getToken = () => {
return localStorage.getItem(TOKEN_KEY);
};
export const removeToken = () => {
localStorage.removeItem(TOKEN_KEY);
};
export const isTokenAvailable = () => {
return !!localStorage.getItem(TOKEN_KEY); // Returns true if token exists
};