Commit 1daa5abf authored by Venkaiah Naidu Singamchetty's avatar Venkaiah Naidu Singamchetty

Merge branch 'initailSetup' into 'master'

Initail setup

See merge request !84
parents 9db2939d c748274f
import React from "react";
function DashboardIcon() {
return (
<svg
className="size-4"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
<polyline points="9 22 9 12 15 12 15 22" />
</svg>
);
}
export default DashboardIcon;
import React from "react";
function ReportsIcon() {
return (
<svg
className="size-4"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z" />
<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z" />
</svg>
);
}
export default ReportsIcon;
...@@ -29,12 +29,12 @@ function Accordion({ title, data ,handleAddActivity,open,handleAccordian}) { ...@@ -29,12 +29,12 @@ function Accordion({ title, data ,handleAddActivity,open,handleAccordian}) {
// } // }
// },[userDetails,id]) // },[userDetails,id])
useEffect(()=>{ // useEffect(()=>{
if(reports?.length !==null){ // if(reports?.length !==null){
dispatch(calculateDefaultScore(reports)) // dispatch(calculateDefaultScore(reports))
dispatch(calculateInitiativeScore(reports)) // dispatch(calculateInitiativeScore(reports))
} // }
},[reports,id]) // },[reports,id])
function handleClick(){ function handleClick(){
handleAccordian(title) handleAccordian(title)
...@@ -46,7 +46,7 @@ function Accordion({ title, data ,handleAddActivity,open,handleAccordian}) { ...@@ -46,7 +46,7 @@ function Accordion({ title, data ,handleAddActivity,open,handleAccordian}) {
{ title: "Score", id: "score", render: (value) => <div className="w-[35px] px-3 bg-blue-400 rounded-full text-white font-bold text-center p-[4px]">{value}</div> }, { title: "Score", id: "score", render: (value) => <div className="w-[35px] px-3 bg-blue-400 rounded-full text-white font-bold text-center p-[4px]">{value}</div> },
{ title: "Comments", id: "comments", render:(value)=><span className="listData" title={value}>{value}</span>}, { title: "Comments", id: "comments", render:(value)=><span className="listData" title={value}>{value}</span>},
]; ];
if(loading && title =="Duties")return <Loading/> if(loading && title =="Duties")return <Loading/>
if(!loading){ if(!loading){
......
...@@ -37,7 +37,7 @@ function Header({ isOpen }) { ...@@ -37,7 +37,7 @@ function Header({ isOpen }) {
}, []); }, []);
return ( return (
<div className="flex items-center justify-between py-5 px-10" > <div className="flex items-center justify-between py-5 px-10 fixed bg-white w-full" >
<Link to={"/dashboard"}><img src="/logo.png" /></Link> <Link to={"/dashboard"}><img src="/logo.png" /></Link>
<div className="flex items-center relative"> <div className="flex items-center relative">
<button ref={logoutRef} className=" -mt-1 text-2xl flex" onClick={() => setOpen(!open)}> <button ref={logoutRef} className=" -mt-1 text-2xl flex" onClick={() => setOpen(!open)}>
......
import React from "react"; import React from "react";
import { Link } from "react-router-dom"; import { Link, useParams } from "react-router-dom";
import { useSelector } from "react-redux"; import SetWindowSize from '../../utils/SetWindowSize';
import DashboardIcon from '../../assets/icons/dashboardIcon';
import ReportsIcon from '../../assets/icons/reportsIcon';
const menus = [
{title: "Dashboard", path: '/dashboard', icon: <DashboardIcon/>},
{title: "Reports", path: '/reportees', icon: <ReportsIcon />}
]
function Sidebar() { function Sidebar() {
const user = useSelector((state) => state.userDetails.user); const url = window.location.href;
const [windowWidth, windowHeight] = SetWindowSize();
return ( return (
<div className="w-[20%] flex items-center flex-col"> <div className="w-[20%] flex items-center flex-col overflow-auto" style={{ height: `calc(${windowHeight}px - 87px)` }}>
<nav <nav
className="hs-accordion-group p-6 w-full flex flex-col flex-wrap" className="hs-accordion-group p-6 w-full flex flex-col flex-wrap"
data-hs-accordion-always-open data-hs-accordion-always-open
> >
<ul className="space-y-1.5"> <ul className="space-y-1.5">
<li> {
<Link menus.map((menu) => (
className={`flex items-center gap-x-3.5 py-2 px-2.5 text-sm text-slate-700 rounded-lg hover:bg-gray-100 `} <li>
to={`/dashboard`} <Link
> className={`flex items-center${url.includes(menu.path) && 'bg-gray-500'} gap-x-3.5 py-2 px-2.5 text-sm text-slate-700 rounded-lg hover:bg-gray-100 `}
<svg to={menu.path}
className="size-4"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
<polyline points="9 22 9 12 15 12 15 22" />
</svg>
Dashboard
</Link>
</li>
<li>
<Link
className={`flex items-center gap-x-3.5 py-2 px-2.5 text-sm text-slate-700 rounded-lg hover:bg-gray-100 `} to={`/reportees`}
>
<svg
className="size-4"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
> >
<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z" /> <span>{menu.icon}</span>
<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z" /> {menu.title}
</svg> </Link>
Reports </li>
</Link> ))
</li> }
</ul> </ul>
</nav> </nav>
</div> </div>
......
...@@ -23,6 +23,3 @@ code { ...@@ -23,6 +23,3 @@ code {
display: inline-block; display: inline-block;
} }
.childDiv {
max-height: calc(100vh - 85px);
}
...@@ -2,9 +2,12 @@ import React, { useState } from 'react' ...@@ -2,9 +2,12 @@ import React, { useState } from 'react'
import Header from '../../components/header'; import Header from '../../components/header';
import Sidebar from '../../components/sidebar'; import Sidebar from '../../components/sidebar';
import LeftSidebar from '../../components/leftSidebar'; import LeftSidebar from '../../components/leftSidebar';
import SetWindowSize from '../../utils/SetWindowSize';
function Layout({children}) { function Layout({children}) {
const [isOpen, setIsOpen] = useState(false) const [isOpen, setIsOpen] = useState(false);
const [windowWidth, windowHeight] = SetWindowSize();
const handleLogoutOpen=()=>{ const handleLogoutOpen=()=>{
setIsOpen(false) setIsOpen(false)
} }
...@@ -12,9 +15,9 @@ function Layout({children}) { ...@@ -12,9 +15,9 @@ function Layout({children}) {
return ( return (
<div className='max-h-[84vh]' onClick={handleLogoutOpen}> <div className='max-h-[84vh]' onClick={handleLogoutOpen}>
<Header isOpen={isOpen} /> <Header isOpen={isOpen} />
<div className="flex"> <div className="flex pt-[85px]">
<Sidebar/> <Sidebar/>
<div className="bg-[#E9EDEE] w-full" style={{height:"88vh"}}> <div className="bg-[#E9EDEE] w-full overflow-auto" style={{ height: `calc(${windowHeight}px - 87px)` }}>
{children} {children}
</div> </div>
{url.includes('/viewreportee') && <LeftSidebar/>} {url.includes('/viewreportee') && <LeftSidebar/>}
......
...@@ -101,7 +101,7 @@ function Exporttable() { ...@@ -101,7 +101,7 @@ function Exporttable() {
if (reportees?.length > 0) { if (reportees?.length > 0) {
return ( return (
<div> <div>
<div className={` overflow-auto sm:rounded-lg p-4 bg-gray-100`}> <div className={` overflow-auto sm:rounded-lg p-4 bg-[#E9EDEE]`}>
<div className="text-blue-800 py-3 pl-2 text-center"> <div className="text-blue-800 py-3 pl-2 text-center">
{" "} {" "}
Genarate Report Genarate Report
......
...@@ -4,7 +4,7 @@ import { useParams, useNavigate } from "react-router"; ...@@ -4,7 +4,7 @@ import { useParams, useNavigate } from "react-router";
import { base_url } from "../../utils/constants"; import { base_url } from "../../utils/constants";
import axios from 'axios'; import axios from 'axios';
import { fetchReportees,setViewReportee } from "../../redux/reducers/reporteesSlice"; import { fetchReportees,setViewReportee } from "../../redux/reducers/reporteesSlice";
import {fetchReporteeActivities} from '../../redux/reducers/viewreporteeSlice' import {fetchReporteeActivities, fetchActivitiesAvg} from '../../redux/reducers/viewreporteeSlice'
import Accordion from "../../components/accordion"; import Accordion from "../../components/accordion";
import {scoreColor} from '../../utils/commonFunctions'; import {scoreColor} from '../../utils/commonFunctions';
...@@ -14,12 +14,10 @@ function Viewreportee() { ...@@ -14,12 +14,10 @@ function Viewreportee() {
const navigate = useNavigate(); const navigate = useNavigate();
const {reportees, viewReportee } = useSelector((state) => state.reportees); const {reportees, viewReportee } = useSelector((state) => state.reportees);
const user = useSelector((state) => state.userDetails.user) const user = useSelector((state) => state.userDetails.user)
const { reports, loading, error } = useSelector((state) => state.reports); const { reports, loading, error, dutiesReports, initiativeReports } = useSelector((state) => state.reports);
const [open, setOpen] = useState({ "accordianOne": false, "accordianTwo": false }); const [open, setOpen] = useState({ "accordianOne": false, "accordianTwo": false });
/*Example post data /*Example post data
{ {
"empId":41689, "empId":41689,
...@@ -27,20 +25,25 @@ function Viewreportee() { ...@@ -27,20 +25,25 @@ function Viewreportee() {
"toDate":"2024-03-11" "toDate":"2024-03-11"
} }
*/ */
const activities = useMemo(() => { const fetchActivities = (type) => {
if (reports) { const data ={
const filtered = Object.groupBy(reports, ({ type }) => type); empId:viewReportee?.empId,
return filtered; types:[type],
page: 1,
perPage: 5
} }
}, [reports, viewReportee]); dispatch(fetchReporteeActivities(data))
}
const handleAccordian = (value) => { const handleAccordian = (value) => {
switch (value) { switch (value) {
case "Duties": case "Duties":
setOpen({ ...open, "accordianOne": !open["accordianOne"], "accordianTwo": false }); setOpen({ ...open, "accordianOne": !open["accordianOne"], "accordianTwo": false });
fetchActivities('duties')
break; break;
case "Initiatives": case "Initiatives":
setOpen({ ...open, "accordianOne": false, "accordianTwo": !open["accordianTwo"] }); setOpen({ ...open, "accordianOne": false, "accordianTwo": !open["accordianTwo"] });
fetchActivities('initiative')
break; break;
default: default:
setOpen({ "accordianOne": false, "accordianTwo": false }); setOpen({ "accordianOne": false, "accordianTwo": false });
...@@ -51,7 +54,6 @@ function Viewreportee() { ...@@ -51,7 +54,6 @@ function Viewreportee() {
if (user) { if (user) {
const data = { const data = {
reportees: user.reportees, reportees: user.reportees,
sort: { type: "empId", order: 1 },
page: 1, page: 1,
perPage: 10, perPage: 10,
}; };
...@@ -68,7 +70,8 @@ function Viewreportee() { ...@@ -68,7 +70,8 @@ function Viewreportee() {
} }
await axios.post(`${base_url}/createActivity`, newData) await axios.post(`${base_url}/createActivity`, newData)
.then(async (result) => { .then(async (result) => {
fetchLatestReporteesData() fetchLatestReporteesData();
fetchActivities(activityData?.type)
}) })
} else { } else {
alert("Please login") alert("Please login")
...@@ -77,14 +80,14 @@ function Viewreportee() { ...@@ -77,14 +80,14 @@ function Viewreportee() {
useEffect(()=>{ useEffect(()=>{
if(reportees.length>0 && viewReportee !== null) if(reportees.length>0 && viewReportee !== null)
dispatch(fetchReporteeActivities({empId:viewReportee?.empId})) dispatch(fetchActivitiesAvg({empId:viewReportee?.empId, types:["duties", "initiative"]}))
},[reportees,viewReportee]) },[viewReportee])
// useEffect(()=>{ useEffect(()=>{
// if(reportees.length){ if(reportees.length){
// dispatch(setViewReportee(viewReportee?.empId)) dispatch(setViewReportee(viewReportee?.empId))
// } }
// },[reportees]) },[reportees])
...@@ -142,10 +145,10 @@ function Viewreportee() { ...@@ -142,10 +145,10 @@ function Viewreportee() {
</div> </div>
<div className=""> <div className="">
<div className=""> <div className="">
<Accordion title="Duties" open={open.accordianOne} handleAccordian={handleAccordian} data={activities?.duties} handleAddActivity={handleAddActivity} /> <Accordion title="Duties" open={open.accordianOne} handleAccordian={handleAccordian} data={dutiesReports} handleAddActivity={handleAddActivity} />
</div> </div>
<div className=""> <div className="">
<Accordion title="Initiatives" open={open.accordianTwo} handleAccordian={handleAccordian} data={activities?.initiative} handleAddActivity={handleAddActivity} /> <Accordion title="Initiatives" open={open.accordianTwo} handleAccordian={handleAccordian} data={initiativeReports} handleAddActivity={handleAddActivity} />
</div> </div>
</div> </div>
</div> </div>
......
...@@ -4,6 +4,8 @@ import axios from "axios"; ...@@ -4,6 +4,8 @@ import axios from "axios";
const initialState = { const initialState = {
reports: null, reports: null,
dutiesReports: null,
initiativeReports: null,
defaultAvgScore:0, defaultAvgScore:0,
initiativeAvgScore:0, initiativeAvgScore:0,
loading: false, loading: false,
...@@ -13,6 +15,12 @@ const initialState = { ...@@ -13,6 +15,12 @@ const initialState = {
export const fetchReporteeActivities = createAsyncThunk("getReports", async (data) => { export const fetchReporteeActivities = createAsyncThunk("getReports", async (data) => {
return await axios return await axios
.post(`${base_url}/getActivities`, data) .post(`${base_url}/getActivities`, data)
.then((response) => response.data?.activities);
});
export const fetchActivitiesAvg = createAsyncThunk("getActivities-avg", async (data) => {
return await axios
.post(`${base_url}/getActivities-avg`, data)
.then((response) => response.data); .then((response) => response.data);
}); });
...@@ -23,39 +31,32 @@ const reportSlice = createSlice({ ...@@ -23,39 +31,32 @@ const reportSlice = createSlice({
resetReports:() => { resetReports:() => {
return initialState return initialState
}, },
calculateDefaultScore:(state, action)=>{
if(action.payload===undefined){
return {...state,defaultAvgScore :0}
}else{
const dutiesItems = action.payload?.filter(item => item.type === "duties");
const totalDutiesScore =dutiesItems?.length? dutiesItems?.reduce((acc, curr) => acc+ Number(curr.score), 0):0;
const defaultAvgScore =totalDutiesScore===0?0: Number(totalDutiesScore) / Number(dutiesItems?.length);
return {...state,defaultAvgScore :Number(defaultAvgScore).toFixed(1)}
}
},
calculateInitiativeScore:(state,action)=>{
if(action.payload===undefined){
return {...state,initiativeAvgScore:0}
}
else{
const initiatiesItems = action.payload?.filter(item => item.type === "initiative");
const totalInitiateScore =initiatiesItems?.length? (initiatiesItems?.reduce((acc, curr) => acc+ Number(curr.score), 0)):0;
const initialAvgScore =totalInitiateScore===0?0: Number(totalInitiateScore) / Number(initiatiesItems?.length) ;
return {...state,initiativeAvgScore :Number(initialAvgScore).toFixed(1)}
}
},
}, },
extraReducers: (builder) => { extraReducers: (builder) => {
builder.addCase(fetchReporteeActivities.pending, (state) => { builder.addCase(fetchReporteeActivities.pending, (state) => {
return {...state,loading :true,error :"loading"} return {...state,loading :true,error :"loading"}
}); });
builder.addCase(fetchReporteeActivities.fulfilled, (state, action) => { builder.addCase(fetchReporteeActivities.fulfilled, (state, action) => {
return {...state,loading :false,error :"",reports:action.payload?.activities} const {type} = action?.payload[0] ?? {}
return {...state,loading :false,error :"", [`${type}Reports`]: action.payload}
}); });
builder.addCase(fetchReporteeActivities.rejected, (state, action) => { builder.addCase(fetchReporteeActivities.rejected, (state, action) => {
return {...state,loading :false,error :action.error || "Something went wrong!",reports:null} return {...state,loading :false,error :action.error || "Something went wrong!",reports:null}
}); });
// getActivities Api
builder.addCase(fetchActivitiesAvg.pending, (state) => {
return {...state,loading :true,error :"loading"}
});
builder.addCase(fetchActivitiesAvg.fulfilled, (state, action) => {
const avgScores = action.payload;
const dutiesAvg = avgScores.find(({type}) => type === "duties")
const initiatieAvg = avgScores.find(({type}) => type === "initiative")
return {...state,loading :false,error :"", defaultAvgScore: dutiesAvg?.avgScore.toFixed(1) || 0, initiativeAvgScore: initiatieAvg?.avgScore.toFixed(1) || 0}
});
builder.addCase(fetchActivitiesAvg.rejected, (state, action) => {
return {...state,loading :false,error :action.error || "Something went wrong!",reports:null}
});
}, },
}); });
......
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