Commit 64d9a47d authored by Venkaiah Naidu Singamchetty's avatar Venkaiah Naidu Singamchetty

Merge branch 'apiserver' into 'master'

average score and name default changed to duties modified modal

See merge request !55
parents 728a8a82 e929b2c3
......@@ -26,6 +26,7 @@
"react-scripts": "5.0.1",
"redux-persist": "^6.0.0",
"redux-thunk": "^3.1.0",
"uuid": "^9.0.1",
"web-vitals": "^2.1.4"
},
"devDependencies": {
......@@ -16398,6 +16399,14 @@
"websocket-driver": "^0.7.4"
}
},
"node_modules/sockjs/node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/source-list-map": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz",
......@@ -17746,9 +17755,13 @@
}
},
"node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
"bin": {
"uuid": "dist/bin/uuid"
}
......
......@@ -21,6 +21,7 @@
"react-scripts": "5.0.1",
"redux-persist": "^6.0.0",
"redux-thunk": "^3.1.0",
"uuid": "^9.0.1",
"web-vitals": "^2.1.4"
},
"scripts": {
......
......@@ -149,7 +149,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", "")) {
if (!_.get(data, "aName", "") || !_.get(data, "aId", "") || !_.get(data, "type", "") || !_.get(data, "score", "") || !_.get(data,"comments","")) {
res.status(401).json({ "error": "Invalid Activity data" });
return;
}
......@@ -158,10 +158,10 @@ app.post('/createActivity',async (req, res) => {
res.status(401).json({ "message": "Score Should be between 1 to 5 or -1 to -5 only" });
return
}
if(data["comments"]===undefined){
res.status(401).json({ "message": "need comments field" });
return
}
// if(data["comments"]===undefined){
// res.status(401).json({ "message": "need comments field" });
// return
// }
data = { ...data, "recorded_date": new Date() };
data = Object.assign(data, { "_id": new ObjectId() })
......@@ -208,15 +208,15 @@ const calculateAverage = async(query) => {
.then(async(result) => {
let activitiesList = result.activities;
let activitiesLength = activitiesList.length;
let score = activitiesList.reduce((acc, curr) => { return acc + curr.score }, 0);
let averageScore = 0;
score < 0
? (averageScore = 0)
: (averageScore = score / activitiesLength);
let score = activitiesList.reduce((acc, curr) => { return acc + Number(curr.score) }, 0);
let averageScore = score / activitiesLength;
// score < 0
// ? (averageScore = 0)
// : (averageScore = score / activitiesLength);
if (averageScore % 1 !== 0) {
averageScore = averageScore.toFixed(1);
}
// if (averageScore % 1 !== 0) {
averageScore = Number(averageScore).toFixed(1);
// }
await db.collection("employees")
.updateOne(query, { $set: { score: Number(averageScore) } })
......
......@@ -30,7 +30,7 @@ function Accordion({ title, data ,handleAddActivity,open,handleAccordian}) {
// },[userDetails,id])
useEffect(()=>{
if(reports !==null){
if(reports?.length !==null){
dispatch(calculateDefaultScore(reports))
dispatch(calculateInitiativeScore(reports))
}
......@@ -46,7 +46,7 @@ function Accordion({ title, data ,handleAddActivity,open,handleAccordian}) {
{ title: "Comments", id: "comments", width: "40%" },
];
if(loading && title =="Default")return <Loading/>
if(loading && title =="Duties")return <Loading/>
if(!loading){
return (
......@@ -56,11 +56,11 @@ function Accordion({ title, data ,handleAddActivity,open,handleAccordian}) {
onClick={handleClick}
type="button"
className="flex items-center rounded-lg w-full py-2 px-2 mt-4 font-medium rtl:text-right bg-white text-gray-500 border border-[#B7B7B7] focus:ring-4 dark:border-gray-700 hover:bg-gray-100 gap-3" data-accordion-target="#accordion-collapse-body-2" aria-expanded="false" aria-controls="accordion-collapse-body-2" >
<div className="w-1/2 text-start ms-2">{title}</div>
<div className="w-1/2 flex justify-between">
Average Score :{title === "Default" ? defaultAvgScore : initiativeAvgScore}
<ModalButton type={`${title === "Default" ? "default" : "initiative"}`} handleAddActivity={handleAddActivity}/>
</div>
<span className="w-1/2 text-start ms-2">{title}</span>
<span className="w-1/2 flex justify-between">
Average Score :{title === "Duties" ? defaultAvgScore : initiativeAvgScore}
<ModalButton type={`${title === "Duties" ? "duties" : "initiative"}`} handleAddActivity={handleAddActivity}/>
</span>
<svg data-accordion-icon className="w-3 h-3 rotate-180 shrink-0" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 10 6">
<path
stroke="currentColor"
......
......@@ -12,7 +12,7 @@ function LeftSidebar() {
return (
<div className=" w-[33%] flex flex-col px-[5px]">
<p className="text-xl text-blue-400 font-semibold pl-4 mt-3">
<p className="text-xl text-blue-400 font-semibold">
My Reportees
</p>
{
......@@ -21,11 +21,11 @@ function LeftSidebar() {
{reportees?.map(({ empName, score, empId }) => (
<Link
to={`/viewreportee/${empId}`}
className={`flex items-center hover:bg-indigo-500 hover:text bg-${Number(id) == empId ? "indigo-200" : "white"
className={`flex items-center hover:bg-blue-400 hover:text-white hover:rounded-2xl bg-${Number(id) == empId ? "indigo-200" : "white"
} p-2 justify-between mb-1 w-full`}
key={empId}
>
<img src="/man.png" width="18px" height="18px" />
{/* <img src="/man.png" width="18px" height="18px" /> */}
<p className="w-[80%] text-left">{empName}</p>
<p className={`w-[30px] h-[30px] rounded-full flex items-center text-white justify-center ${scoreColor(score)}`}>
{score}
......
import axios from "axios";
import React, { useMemo, useEffect, useState, useCallback } from "react";
import { base_url } from "../../utils/constants";
import { v4 as uuidv4 } from 'uuid';
import Loading from "../loading Component/Loading";
export default function MyModal({ visible, onClose ,type,handleAddActivity}) {
export default function MyModal({ visible, onClose, type, handleAddActivity }) {
const [activitiesList, setActivitiesList] = useState([])
const [enableSubmit,setEnableSubmit]=useState(false)
const [scoreType,setScoreType]=useState(1)
const [activityData,setActivityData]=useState({aName:"",aId:"",type:type,score:0,comments:""})
const [activityType,setActivtyType]=useState("")
const [enableSubmit, setEnableSubmit] = useState(false)
const [activityData, setActivityData] = useState({ aName: "", aId: "", type: type, score: 0, comments: "" })
const [activityType, setActivtyType] = useState("")
const [showCustActivity, setShowActivity] = useState(false);
const [custActivity, setCustActivity] = useState('')
const [modalLoading, setModalLoading] = useState(true)
const [scoreRender, setScoreRender] = useState([]);
const [showScore, setShowScore] = useState(false)
const getActivitysList= async(type)=>{
const activities=await axios.get(`${base_url}/activities`)
const response= await activities.data.filter((item)=>item.type==type)
const getActivitysList = async (type) => {
const activities = await axios.get(`${base_url}/activities`)
const response = await activities.data.filter((item) => item.type == type)
setActivitiesList(response)
setModalLoading(false)
}
const handleActivityName = (e) => {
setActivityData({...activityData,aName:e.target.value,aId:e.target.options[e.target.selectedIndex].id})
setActivityData({ ...activityData, aName: e.target.value, aId: e.target.options[e.target.selectedIndex].id })
}
const handleScoreChange=(value)=>{
setActivityData({...activityData,score:(scoreType)*(value)})
const handleCustumActivity = (e) => {
const randomId = uuidv4();
setActivityData({ ...activityData, aName: e.target.value, aId: randomId })
}
function handlePerformance (value){
setScoreType(value)
const handleScoreChange = (value) => {
setActivityData({ ...activityData, score: value })
}
const handlePerformance=(value)=> {
let appreciateScores = [0, 1, 2, 3, 4, 5]
let depreciateScores = [-0, -1, -2, -3, -4, -5]
if (value == 1) {
setActivityData({ ...activityData, score: 0 })
setScoreRender(appreciateScores)
setShowScore(true)
}
else if (value == -1) {
setActivityData({ ...activityData, score: 0 })
setScoreRender(depreciateScores)
setShowScore(true)
}
}
const handleComments=(e)=>{
// e.stopPropagation()
setActivityData({...activityData,comments:e.target.value})
const handleComments = (e) => {
setActivityData({ ...activityData, comments: e.target.value })
}
const handleSubmit=(e)=>{
const handleSubmit = (e) => {
onClose()
setShowActivity(false)
handleAddActivity(activityData)
}
useEffect(()=>{handleScoreChange(activityData.score)},[scoreType])
useEffect(() => {
if (activityData.aName !== "" && activityData.aId !== "" && activityData.score != 0 || -0) {
if (activityData.aName !== "" && activityData.aId !== "" && activityData.comments !== "" && activityData.score != (0 || -0)) {
setEnableSubmit(true);
} else {
setEnableSubmit(false);
}
}, [activityData]);
const SentenceCase=(type)=>{
let str=type;
const SentenceCase = (type) => {
let str = type;
setActivtyType(str.charAt(0).toUpperCase() + str.slice(1).toLowerCase())
}
useEffect(()=>{
useEffect(() => {
SentenceCase(type)
if (visible===false){
setActivityData({aName:"",aId:"",type:type,score:0,comments:""})
}else{
if (visible === false) {
setActivityData({ aName: "", aId: "", type: type, score: 0, comments: "" })
} else {
getActivitysList(type);
setModalLoading(true)
}
},[visible,type]);
}, [visible, type]);
const handleCustBtn = (e) => {
e.preventDefault();
setShowActivity(!showCustActivity);
setActivityData({ ...activityData, aName: "", aId: "" })
}
......@@ -76,50 +99,46 @@ export default function MyModal({ visible, onClose ,type,handleAddActivity}) {
return (
<>
<div className="absolute w-full h-full inset-0 bg-black bg-opacity-25 backdrop-blur-sm flex items-center justify-center" onClick={(e) => e.stopPropagation()}>
<div className="bg-white rounded-md lg:w-4/12 sm:w-100">
<div className=" text-white py-3 pl-2 bg-blue-500 rounded-md">{activityType} Activity</div>
{
(!modalLoading) ? <div className="bg-white rounded-md lg:w-4/12 sm:w-100">
<div className=" text-white py-3 pl-2 bg-blue-500 rounded-md text-start">{activityType}</div>
<div>
<div>
{
(!modalLoading) ? <form className=" p-2 max-w-sm mx-auto text-[12px]" onClick={(e) => e.stopPropagation()}>
<form className=" p-2 max-w-sm mx-auto text-[12px]" onClick={(e) => e.stopPropagation()}>
<div className="flex items-center justify-between my-5">
<label htmlFor="countries">SELECT ACTIVITY: </label>
<select disabled={showCustActivity} className="bg-gray-50 ml-2 w-7/12 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" onChange={(e)=>handleActivityName(e)}>
<label htmlFor="countries">SELECT ACTIVITY<span className="text-[15px]">*</span>: </label>
<select disabled={showCustActivity} className="bg-gray-50 ml-2 w-6/12 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" onChange={(e) => handleActivityName(e)} value={activityData.aName}>
<option id="" value="">Select</option>
{
activitiesList && activitiesList.map((activity)=><option className=" w-7/12" key={activity.aId} id={activity.aId} value={activity.aName}>{activity.aName}</option>)
activitiesList && activitiesList.map((activity) => <option className=" w-7/12" key={activity.aId} id={activity.aId} value={activity.aName}>{activity.aName}</option>)
}
</select>
<button onClick={(e) => {e.preventDefault(); setShowActivity(!showCustActivity)}} className="bg-blue-400 ml-2 w-5/12 text-white px-2 py-1 rounded hover:scale-95 transition text-sm">Custom Activity</button>
<button onClick={(e) => { handleCustBtn(e) }} className="bg-blue-400 ml-2 w-2/12 text-white py-1 rounded hover:scale-95 transition text-sm">Custom</button>
</div>
<div className={`flex items-center ${!showCustActivity && 'hidden'}`}>
<label className={`font-medium dark:text-gray-300 mr-2`}>Custom Activity:</label>
<input type="text" value={custActivity} placeholder="Enter Activity name" name="performance" className={`border border-gray-300 rounded p-2 `} onChange={(e)=>setCustActivity(e.target.value)}/>
<label className={`font-medium dark:text-gray-300 mr-2`}>Custom 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 ">
<label htmlFor="appreciate" className=" font-medium dark:text-gray-300">APPRECIATION:</label>
<input id="appreciate" type="radio" value="appreciate" name="performance" className="w-4 h-4 m-3 text-blue-600 bg-gray-100 border-gray-300 dark:bg-gray-700 dark:border-gray-600" onChange={()=>handlePerformance(1)}/>
<label htmlFor="depreciate" className="ms-2 font-medium dark:text-gray-300">DEPRECIATION:</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 dark:bg-gray-700 dark:border-gray-600" onChange={()=>handlePerformance(-1)} />
<label htmlFor="appreciate" className=" font-medium dark:text-gray-300">APPRECIATION<span className="text-[15px]">*</span>:</label>
<input id="appreciate" type="radio" value="appreciate" name="performance" className="w-4 h-4 m-3 text-blue-600 bg-gray-100 border-gray-300 dark:bg-gray-700 dark:border-gray-600" onChange={() => handlePerformance(1)} />
<label htmlFor="depreciate" className="ms-2 font-medium dark:text-gray-300">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 dark:bg-gray-700 dark:border-gray-600" onChange={() => handlePerformance(-1)} />
</div>
<div className="flex ">
<span>SCORE: </span>
<select className="border w-1/5 ms-1" onChange={(e)=>handleScoreChange(e.target.value)}>
<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}>
<option value={0}>Select</option>
<option value={1}>1</option>
<option value={2}>2</option>
<option value={3}>3</option>
<option value={4}>4</option>
<option value={5}>5</option>
{
scoreRender && scoreRender.map((score) => <option value={score}>{score}</option>)
}
</select>
</div>
<div className="flex items-center my-5">
<label htmlFor="comments" className="block w-3/12 mb-20 text-start font-medium dark:text-white">COMMENTS :</label>
<textarea id="comments" style={{resize:"none"}} rows="4" className="block ml-2 p-2.5 w-9/12 bg-gray-50 rounded-lg border border-gray-300 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="Activity comments (optional)" onChange={(e)=>handleComments(e)}
<label htmlFor="comments" className="block w-3/12 mb-20 text-start font-medium dark:text-white">COMMENTS<span className="text-[15px]">*</span>:</label>
<textarea id="comments" style={{ resize: "none" }} rows="4" className="block ml-2 p-2.5 w-9/12 bg-gray-50 rounded-lg border border-gray-300 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="Comments" onChange={(e) => handleComments(e)}
onClick={(e) => e.stopPropagation()}
onKeyDown={(e) => {
// Prevent propagation of space key press
if (e.key === " ") {
// e.preventDefault()
e.stopPropagation();
......@@ -134,19 +153,18 @@ export default function MyModal({ visible, onClose ,type,handleAddActivity}) {
}}></textarea>
</div>
<div className="flex items-center justify-end mb-3">
<button onClick={() => {setShowActivity(false); onClose()}} className="px-3 py-2 rounded-md bg-gray-700 text-white ">Cancel</button>
<button onClick={() => { setShowActivity(false); onClose() }} className="px-3 py-2 rounded-md bg-gray-700 text-white ">Cancel</button>
{
enableSubmit?<button type="button" className="px-3 py-2 ml-5 bg-blue-500 text-white rounded-md" onClick={handleSubmit}>Submit</button>:
enableSubmit ? <button type="button" className="px-3 py-2 ml-5 bg-blue-500 text-white rounded-md" onClick={handleSubmit}>Submit</button> :
<button type="button" className="px-3 py-2 ml-5 bg-gray-400 text-white rounded" disabled={!enableSubmit} title="Please fill all fileds to submit">Submit</button>
}
</div>
</form>:null
}
</div>
</form>
</div>
</div>
</div> : null
}
</div>
</>
)
......
......@@ -3,27 +3,27 @@ import Loading from "../loading Component/Loading";
function Table({headers, data,loading, maxHeight}) {
if(loading) return <Loading/>
else
return (
<div className={` overflow-auto sm:rounded-lg p-4 bg-gray-100`}>
<table className="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400 bg-transparent justify-center border-separate border-spacing-y-2">
<thead className="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-white">
<tr className="mb-2">
{headers.map((item,index) => (
{headers?.map((item,index) => (
<th key={index} scope="col" className={`px-6 py-4 w-[${item.width}]`} >
{item.title}
</th>
))}
</tr>
</thead>
{ (data?.length)?
<tbody>
{
(data?.length)?<tbody>
{
data?.map((item, index) => (
<tr className="bg-white dark:bg-gray-800 dark:border-gray-700 dark:text-white hover:bg-gray-50 dark:hover:bg-gray-600">
<tr key={item.id} className="bg-white dark:bg-gray-800 dark:border-gray-700 dark:text-white hover:bg-gray-50 dark:hover:bg-gray-600">
{
headers?.map(({render, id}) => (
<td className="px-6 py-4 listData" >{render ? render(item[id]) : item[id] === "" ? "NA" : item[id] }</td>
<td key={`${item.id}_${id}`} className="px-6 py-4 listData" >{render ? render(item[id]) : item[id] === "" ? "NA" : item[id] }</td>
))
}
</tr>
......
......@@ -38,10 +38,10 @@ function Reports() {
const handleAccordian = (value) => {
switch (value) {
case "Default":
case "Duties":
setOpen({ ...open, "accordianOne": !open["accordianOne"], "accordianTwo": false });
break;
case "Initiative":
case "Initiatives":
setOpen({ ...open, "accordianOne": false, "accordianTwo": !open["accordianTwo"] });
break;
default:
......@@ -151,14 +151,11 @@ function Reports() {
</div>
</div>
<div className="">
{/* <div className="container mx-auto mt-4 flex justify-end pe-4">
<DateRangePicker getReports={getReports} />
</div> */}
<div className="">
<Accordion title="Default" open={open.accordianOne} handleAccordian={handleAccordian} data={activities?.default} handleAddActivity={handleAddActivity} />
<Accordion title="Duties" open={open.accordianOne} handleAccordian={handleAccordian} data={activities?.duties} handleAddActivity={handleAddActivity} />
</div>
<div className="">
<Accordion title="Initiative" open={open.accordianTwo} handleAccordian={handleAccordian} data={activities?.initiative} handleAddActivity={handleAddActivity} />
<Accordion title="Initiatives" open={open.accordianTwo} handleAccordian={handleAccordian} data={activities?.initiative} handleAddActivity={handleAddActivity} />
</div>
</div>
</div>
......
......@@ -24,16 +24,25 @@ const reportSlice = createSlice({
return initialState
},
calculateDefaultScore:(state, action)=>{
const defaultItems = action.payload?.filter(item => item.type === "default");
const totalDefaultScore = defaultItems?.reduce((acc, curr) => acc+ curr.score, 0);
const defaultAvgScore = totalDefaultScore > 0 ? totalDefaultScore / defaultItems.length : 0;
return {...state,defaultAvgScore :defaultAvgScore.toFixed(1)}
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)=>{
const defaultItems = action.payload?.filter(item => item.type === "initiative");
const totalDefaultScore = defaultItems?.reduce((acc, curr) => acc+ curr.score, 0);
const initialAvgScore = totalDefaultScore > 0 ? totalDefaultScore / defaultItems.length : 0;
return {...state,initiativeAvgScore :initialAvgScore.toFixed(1)}
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)}
}
},
},
......
const scoreColor = (value) => {
if (value >= 0 && value < 1) {
if (value < 1) {
return 'bg-red-400';
} else if (value >= 1 && value < 2) {
return 'bg-red-300';
......
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