Commit d858670a authored by Shiva Komirishetti's avatar Shiva Komirishetti

Role based menu changes

parent 699a1060
......@@ -19,9 +19,12 @@
"react": "^18.2.0",
"react-datepicker": "^6.3.0",
"react-dom": "^18.2.0",
"react-js-loader": "^0.1.3",
"react-modal": "^3.16.1",
"react-redux": "^9.1.0",
"react-router-dom": "^6.22.3",
"react-scripts": "5.0.1",
"react-toastify": "^10.0.5",
"redux-persist": "^6.0.0",
"redux-thunk": "^3.1.0",
"uuid": "^9.0.1",
......
......@@ -155,7 +155,7 @@ app.post('/createActivity',async (req, res) => {
let { data } = req.body;
//data validation
if (!_.get(data, "aName", "") || !_.get(data, "aId", "") || !_.get(data, "type", "") || !_.get(data, "score", "") || !_.get(data,"comments","") ||!_.get(data,"ratedBy","") ) {
if (!_.get(data, "aName", "") || !_.get(data, "type", "") || !_.get(data, "score", "") || !_.get(data,"comments","") ||!_.get(data,"ratedBy","") ) {
res.status(401).json({ "error": "Invalid Activity data" });
return;
}
......
......@@ -7,8 +7,7 @@ import './App.css';
import PageNotFound from './pages/pagenotfound/PageNotFound';
import Exporttable from './pages/reportexport'
import AdminProfile from './pages/adminProfile';
import Adminreports from './pages/adminreports';
import Admin from './pages/admin';
import ActivityList from './pages/activityList';
function App() {
return (
......@@ -24,10 +23,8 @@ function App() {
<Route path="/viewreportee" element={<Viewreportee/>}/>
{/* fetch reports */}
<Route path="/reportees" element={<Exporttable/>}/>
{/* Admin reports */}
<Route path="/adminreportees" element={<Adminreports/>}/>
{/* Activity List */}
<Route path="/Admin" element={<Admin/>}/>
<Route path="/activityList" element={<ActivityList/>}/>
</Route>
<Route path="/*" element={<PageNotFound/>}/>
</Routes>
......
import React from 'react';
import Modal from 'react-modal';
const customStyles = {
content: {
top: '50%',
left: '50%',
right: 'auto',
bottom: 'auto',
marginRight: '-50%',
transform: 'translate(-50%, -50%)',
},
};
function BaseModal({children, open}) {
return (
<Modal
isOpen={open}
style={customStyles}
>
{children}
</Modal>
);
}
export default BaseModal;
\ No newline at end of file
import React from 'react';
import BaseModal from '../baseModal';
function ConfirmPopup(props) {
const {open, header, content, handleCancel, handleConfirm} = props;
return (
<BaseModal
open={open}
>
<div className='w-[350px]'>
<h3 className='font-semibold'>{header}</h3>
{content && <p className='my-4'> {content}</p>}
<div className='flex justify-end'>
<button className="px-3 py-2 ml-5 bg-gray-500 text-white rounded-md hover:bg-gray-400" onClick={handleCancel}>Cancel</button>
<button className="px-3 py-2 ml-5 bg-blue-500 text-white rounded-md hover:bg-blue-300" onClick={handleConfirm}>Confirm</button>
</div>
</div>
</BaseModal>
)
}
export default ConfirmPopup
import React, { useEffect, useState, useCallback } from "react";
import axios from "axios";
import { base_url } from "../../utils/constants";
import { fetchData } from "../../pages/admin";
import { toast } from 'react-toastify';
const MyCreateModal = ({ setShowMyModal,handleRefresh}) => {
const [formData, setFormData] = useState({
......@@ -48,8 +48,8 @@ const MyCreateModal = ({ setShowMyModal,handleRefresh}) => {
try {
// console.log(formData.atype);
await axios.post(`${base_url}/addMasterActivity`, formData)
.then((res)=>fetchData())
setShowMyModal(false);
toast.success("Activity created successfully.")
setFormData({
atype: "",
aName: "",
......
......@@ -86,11 +86,11 @@ export default function MyModal({ visible, onClose, type, handleAddActivity }) {
setActivtyType(str.charAt(0).toUpperCase() + str.slice(1).toLowerCase())
}
useEffect(() => {
if (type === "duties") {
setDisableAppreciate(true);
} else {
setDisableAppreciate(false);
}
// if (type === "duties") {
// setDisableAppreciate(true);
// } else {
// setDisableAppreciate(false);
// }
SentenceCase(type)
if (visible === false) {
setActivityData({ aName: "", ratedBy: "", type: type, score: 0, comments: "" })
......@@ -107,6 +107,10 @@ export default function MyModal({ visible, onClose, type, handleAddActivity }) {
setActivityData({ ...activityData, aName: "" })
}
const disableState = (value) => {
const state = activitiesList?.find((activity) => activity.aName === activityData.aName);
if(state) return state[value]
}
if (!visible) return null;
......@@ -127,19 +131,19 @@ export default function MyModal({ visible, onClose, type, handleAddActivity }) {
{
activitiesList && activitiesList.map((activity) => <option className=" w-7/12" value={activity.aName}>{activity.aName}</option>)
}
<option value="custom" className={`${showCustActivity || type == "duties" && 'hidden'}`}>Add Activity</option>
{/* <option value="custom" className={`${showCustActivity || type == "duties" && 'hidden'}`}>Add Activity</option> */}
</select>
</div>
<div className={`flex items-center ${!showCustActivity && 'hidden'}`}>
<label className={`font-medium mr-2`}>Activity<span className="text-[15px]">*</span>:</label>
<input type="text" value={activityData.aName} placeholder="Enter Activity name" name="performance" className={`border border-gray-300 rounded p-2 `} onChange={(e) => handleCustumActivity(e)} />
</div>
<div className="flex items-center mb-4 ">
{activityData.aName && <div className="flex items-center mb-4 ">
<label htmlFor="appreciate" className="font-medium">APPRECIATION<span className="text-[15px]">*</span>:</label>
<input id="appreciate" disabled={disableAppreciate} type="radio" value="appreciate" name="performance" className="w-4 h-4 m-3 text-blue-600 bg-gray-100 border-gray-300 " onChange={() => handlePerformance(1)} />
<input id="appreciate" disabled={!disableState('appreciate')} type="radio" value="appreciate" name="performance" className="w-4 h-4 m-3 text-blue-600 bg-gray-100 border-gray-300 " onChange={() => handlePerformance(1)} />
<label htmlFor="depreciate" className="ms-2 font-medium ">DEPRECIATION<span className="text-[15px]">*</span>:</label>
<input id="depreciate" type="radio" value="depreciate" name="performance" className="w-4 h-4 m-3 text-blue-600 bg-gray-100 border-gray-300 " onChange={() => handlePerformance(-1)} />
</div>
<input id="depreciate" disabled={!disableState('depreciate')} type="radio" value="depreciate" name="performance" className="w-4 h-4 m-3 text-blue-600 bg-gray-100 border-gray-300 " onChange={() => handlePerformance(-1)} />
</div>}
<div className={`flex ${!showScore && 'hidden'}`}>
<span>SCORE<span className="text-[15px]">*</span>: </span>
<select className="border w-1/5 ms-1" onChange={(e) => handleScoreChange(e.target.value)} value={activityData.score}>
......
......@@ -4,16 +4,12 @@ import { Link, useParams, useLocation } from "react-router-dom";
import SetWindowSize from '../../utils/SetWindowSize';
import DashboardIcon from '../../assets/icons/dashboardIcon';
import ReportsIcon from '../../assets/icons/reportsIcon';
import AdminProfileIcon from '../../assets/icons/adminProfileIcon'; // Assuming you have an icon for Admin Profile
import Admin from "../../pages/admin";
const menus = [
{ title: "Dashboard", path: '/dashboard', selectPaths: ['/dashboard'], icon: <DashboardIcon />, role:[2, 3] },
{ title: "My Reportees", path: '/myreportees', selectPaths: ['/myreportees', '/viewreportee'], icon: <ReportsIcon />, role:[2] },
{ title: "Reports", path: '/reportees', selectPaths:['/reportees'], icon: <ReportsIcon />, role:[2] },
{ title: "Activity List", path: '/admin', selectPaths:['/admin'], icon: <ReportsIcon />, role:[1] },
{ title: "Reports", path: '/adminreportees', selectPaths:['/adminreportees'], icon: <ReportsIcon />, role:[1] },
{ title: "Activity List", path: '/activityList', selectPaths:['/activityList'], icon: <ReportsIcon />, role:[1] },
{ title: "Reports", path: '/reportees', selectPaths:['/reportees'], icon: <ReportsIcon />, role:[2,1] }
]
function Sidebar() {
......
......@@ -42,4 +42,8 @@ code {
to {
transform: rotate(360deg);
}
}
.ReactModal__Overlay {
background-color: rgb(153 150 150 / 55%) !important;
}
\ No newline at end of file
import React, { useEffect, useState } from "react";
import { useSelector, useDispatch } from "react-redux"; // Import useDispatch
import CreateActivityButton from '../../components/modal/createActivityButton';
import Table from '../../components/table';
import Loading from "../../components/loading Component/Loading";
import CreateActivityButton from '../../components/modal/createActivityButton.jsx';
import Table from '../../components/table/index.jsx';
import Loading from "../../components/loading Component/Loading.jsx";
import { styles } from './styles.js';
import { convertToString } from "../../utils/commonFunctions";
import axiosApi from '../../api/axiosConfig'
import { convertToString } from "../../utils/commonFunctions.js";
import axiosApi from '../../api/axiosConfig.js'
import ConfirmPopup from "../../components/confirmationPopup/index.jsx";
import { toast } from 'react-toastify';
// Define fetchData function
export const fetchData = async () => {
const ActivityList = () => {
const [activitiesList, setActivitiesList] = useState([]);
const { loading } = useSelector((state) => state.reportees);
const [deleteId, setDeleteId] = useState(null);
const [deleteActivity, setDeleteActivity] = useState(null)
const dispatch = useDispatch(); // Initialize useDispatch
const fetchActivityData = async () => {
try {
const response = await axiosApi.get(`/activities`);
//setActivitiesList(response.data)
return response.data;
setActivitiesList(response.data)
} catch (error) {
console.error('Error fetching data:', error);
// console.error('Error fetching data:', error);
throw error; // Rethrow the error for handling by the caller if needed
}
};
const Admin = () => {
const [activitiesList, setActivitiesList] = useState([]);
const [refresh,setRefresh]=useState(true)
const { loading } = useSelector((state) => state.reportees);
const dispatch = useDispatch(); // Initialize useDispatch
const handleRefresh=()=>{
setRefresh(!refresh)
}
useEffect(() => {
// Call fetchData when the component mounts
fetchData().then((res)=>setActivitiesList(res))
}, [refresh]);
fetchActivityData();
}, []);
const handleDelete = async (_id) => {
try {
//console.log("Deleting activity with id:", _id);
await axiosApi.put(`/deleteMasterActivity`, { ObjectId: _id });
setRefresh(!refresh)
// console.log("Deleted successfully");
toast.success('Activity deleted successfully.')
fetchActivityData();
} catch (error) {
// console.error("Error deleting activity:", _id);
// console.error("Error:", error);
} finally {
setDeleteId(null);
}
};
......@@ -60,17 +53,10 @@ const handleRefresh=()=>{
{
title: "Actions",
id: "_id",
render: (id) => (
render: (id, item) => (
<button
className="bg-red-400 text-white rounded-md px-1 py-1 flex items-center justify-center w-[40px]"
onClick={() => {
if (id) {
handleDelete(id);
//console.log(id)
} else {
// console.error("Item or item._id is undefined");
}
}}
onClick={() => {setDeleteId(id); setDeleteActivity(item.aName)}}
>
X
</button>
......@@ -87,12 +73,19 @@ const handleRefresh=()=>{
ACTIVITY LIST
</div>
<div className="flex" style={{ justifyContent: 'flex-end', marginBottom: "10px" }}>
<CreateActivityButton handleRefresh={handleRefresh} />
<CreateActivityButton handleRefresh={() => fetchActivityData()} />
</div>
<Table headers={headers} loading={loading} data={activitiesList} />
</div>
<ConfirmPopup
open={deleteId ? true : false}
header='Delete Activity'
content={`Are you sure want to delete ${deleteActivity} activity.`}
handleCancel={() => setDeleteId(null)}
handleConfirm={() => handleDelete(deleteId)}
/>
</div>
);
};
export default Admin;
export default ActivityList;
export const styles = {
createActivityContainer: "overflow-auto sm:rounded-lg p-4",
textBlueHeading: "text-blue-800 py-3 pl-2 text-center fond-bold text-2xl",
};
\ No newline at end of file
export const styles = {
createActivityContainer: "overflow-auto sm:rounded-lg p-4 bg-[#E9EDEE] ",
textBlueHeading: "text-blue-800 py-3 pl-2 text-center fond-bold text-2xl",
};
\ No newline at end of file
......@@ -130,6 +130,7 @@ function Adminreports() {
{
title: "Actions",
id: "_id",
hide: false,
render: (id) => (
<button
className="bg-red-400 text-white rounded-md px-1 py-1 flex items-center justify-center w-[40px]"
......
......@@ -133,7 +133,7 @@ function Dashboard() {
<label>Search :</label>
<input placeholder="Name/Id/Designation/Role" value={inputValue} onChange={handleChange} type="text" className="p-1 px-2 border rounded ml-2 placeholder:text-[14px]"/>
</div>
<Table headers={headers} data={reportees} loading={loading} handleSorting={handleSort}/>
<Table headers={headers} data={reportees} loading={loading} handleSorting={handleSort} loadingRows={10} />
<div className="">
{reportees.length>0 && pagesCount>1 && (
......
......@@ -45,7 +45,7 @@ function Home() {
useEffect(()=>{
if (userDetails?.user!=null){
if(userDetails?.user.roleId === 1)
navigate("/admin")
navigate("/activityList")
else
navigate("/dashboard")
}
......
import React, { useEffect, useState } from "react";
import React, { useEffect, useMemo, useState } from "react";
import axiosApi from '../../api/axiosConfig';
import { useDispatch, useSelector } from "react-redux";
import { fetchReportesActivitiesData, resetReporteesTableData, resetActivitiesData } from "../../redux/reducers/exporttableslice";
import { fetchReportees } from "../../redux/reducers/reporteesSlice";
import { convertUTCToLocal } from "../../utils/commonFunctions";
import ConfirmPopup from "../../components/confirmationPopup/index.jsx";
import { toast } from 'react-toastify';
import Table from "../../components/table";
import DownloadIcon from '../../assets/icons/downloadIcon';
import jsPDF from 'jspdf';
......@@ -17,13 +19,15 @@ function Exporttable() {
const { reportees, loading, totalCount, currPage, pagesCount } = useSelector(
(state) => state.reportees
);
const [selectedEmployee, setSelectedEmployee] = useState(0);
const [fromDate, setFromDate] = useState("");
const [toDate, setToDate] = useState("");
const [inputValue, setInputValue] = useState('');
const [pdfLoading, setPdfLoading] = useState(false);
const [selectedDate, setSelectedDate] = useState(null)
const [employees, setEmployees] = useState(null);
const [deleteId, setDeleteId] = useState(null);
const [deleteActivityName, setDeleteActivityName] = useState(null)
useEffect(() => {
if(selectedEmployee && fromDate && toDate) {
......@@ -66,19 +70,45 @@ function Exporttable() {
useEffect(() => {
if (user) {
let data = {
reportees: user.reportees,
page: 1,
perPage: 100000000, //user.reportees.length,
getMasterData: true
};
dispatch(fetchReportees(data));
if(user.roleId !== 1){
let data = {
reportees: user.reportees,
page: 1,
perPage: 100000000, //user.reportees.length,
getMasterData: true
};
dispatch(fetchReportees(data))
} else {
const getEmployees = async () => {
try {
const res = await axiosApi.get(`/employees`);
const data = res?.data.filter((emp) => emp.roleId !== 1)
setEmployees(data)
} catch (error) {
// console.error("Error:", error);
}
}
if(employees === null) getEmployees();
}
}
return(() => {
dispatch(resetReporteesTableData())
})
}, []);
const handleReportieDelete = async (id) => {
try {
await axiosApi.put(`/deleteActivity`, { ObjectId: id, empId: Number(selectedEmployee) });
toast.success('Activity deleted successfully.')
dispatch(fetchReportesActivitiesData({ empId: Number(selectedEmployee), fromDate, toDate }));
} catch (error) {
// console.error("Error deleting activity:", id);
// console.error("Error:", error);
} finally {
setDeleteId(null);
}
};
const headers = [
{ title: "Activity Name", id: "aName" },
......@@ -106,6 +136,27 @@ function Exporttable() {
</span>
),
},
{
title: "Actions",
id: "_id",
hide: user.roleId !== 1 ? true : false,
render: (id, item) => (
<button
className="bg-red-400 text-white rounded-md px-1 py-1 flex items-center justify-center w-[40px]"
onClick={() => {
// if (id && selectedEmployee) {
// handleReportieDelete(id,selectedEmployee);
// } else {
// // console.error("Item or item._id is undefined");
// }
setDeleteId(id);
setDeleteActivityName(item.aName)
}}
>
X
</button>
)
},
];
const periodOptions = ['Past 1 month', 'Past 3 months', 'Past 6 months', 'Past 1 year']
......@@ -163,9 +214,10 @@ function Exporttable() {
const handleSelectedName=(value)=>{
if(value!=="")
setSelectedEmployee(value)
}
const employeeList = user?.roleId === 1 ? employees : reportees;
return (
<div>
<div className={styles.genarateReportContainer}>
......@@ -190,8 +242,8 @@ function Exporttable() {
<option id="" value="">
Select
</option>
{reportees &&
reportees.map((reportee) => (
{employeeList &&
employeeList.map((reportee) => (
<option
className="text-pretty"
key={reportee?.empId}
......@@ -246,6 +298,13 @@ function Exporttable() {
</div>
<Table headers={headers} loading={loading} data={activitiesData} />
</div>
<ConfirmPopup
open={deleteId ? true : false}
header='Delete Activity'
content={`Are you sure want to delete ${deleteActivityName} activity.`}
handleCancel={() => setDeleteId(null)}
handleConfirm={() => handleReportieDelete(deleteId,selectedEmployee)}
/>
</div>
);
......
......@@ -83,6 +83,7 @@ function Viewreportee() {
if(reporteeId) {
fetchViewReporteeData(reporteeId)
dispatch(fetchActivitiesAvg({empId:reporteeId, types:["duties", "initiative"]}))
setOpen({ "accordianOne": false, "accordianTwo": false });
}
}, [reporteeId])
......
......@@ -49,8 +49,8 @@ const reportSlice = createSlice({
});
builder.addCase(fetchActivitiesAvg.fulfilled, (state, action) => {
const avgScores = action.payload;
const dutiesAvg = avgScores.find(({type}) => type === "duties")
const initiatieAvg = avgScores.find(({type}) => type === "initiative")
const dutiesAvg = avgScores.find(({_id}) => _id === "duties")
const initiatieAvg = avgScores.find(({_id}) => _id === "initiative")
return {...state,loading :false,error :"", defaultAvgScore: dutiesAvg?.avgScore.toFixed(1) || 0, initiativeAvgScore: initiatieAvg?.avgScore.toFixed(1) || 0}
});
builder.addCase(fetchActivitiesAvg.rejected, (state, action) => {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment