loginloader changed color indications changed

parent e929b2c3
# nisum-scorecard
Nisum ScoreCard Demo Documentation
Project Demo Deployment Link DEV team
URL : https://nisumscorecard.netlify.app/
Project Demo Deployment Link for QA team testing
URL : https://nisumscorecard-qa.netlify.app/
API's for QA Testing Team Usage
GET API’s :
1. To get all employees data
https://nisumscorecardservertesting.netlify.app/.netlify/functions/api/employees
2. To get individual employee data
https://nisumscorecardservertesting.netlify.app/.netlify/functions/api/employee/:id
3. To get master data of activities with types
https://nisumscorecardservertesting.netlify.app/.netlify/functions/api/activities
POST API's
1. POST api to validate login into dashboard
https://nisumscorecardservertesting.netlify.app/.netlify/functions/api/login
Data In body
example: {"empId":41689}
2. POST api to get reportees data under the employee who logged in. The data we send in the body should contain at least reportees filed with an array of employee empId, remaining all fields are optional.
https://nisumscorecardservertesting.netlify.app/.netlify/functions/api/getreportees
Data In body Example :
{
"reportees":[41689,41716,41710,41750,41751,41714],
"sort":{"type":"empId","order":-1}
,"page":1,"perPage":10,
"searchText":"eng"
}*/
3. POST method to add activity added by manager to his reportees. Id should contain all the fields given below in the example or else it throws an error.
Note: Users can add a custom aName field , from frontend a unique id will be generated and added as aID.
https://nisumscorecardservertesting.netlify.app/.netlify/functions/api/createActivity
Data In body Example :
//Example of post Data
/*
{
"empId":41689,
"data":{
"aName":"Approval of timesheet",
"aId":"D001",
"type":"duties",
"score":3,
"comments":""
}
}
Actual Activities Data this to be used when trying to create a activity to a employee:
[{"_id":"65f19252ecd2b756fab896b8","type":"duties","aId":"D001","aName":"Submission timesheet"},
{"_id":"65f19252ecd2b756fab896b9","type":"duties","aId":"D002","aName":"Successful deliveries"},
{"_id":"65f19252ecd2b756fab896bb","type":"duties","aId":"D004","aName":"Approval of timesheet"},
{"_id":"65f19252ecd2b756fab896bd","type":"initiative","aId":"I002","aName":"POC's"},
{"_id":"65f19252ecd2b756fab896ba","type":"duties","aId":"D003","aName":"Vacation/Unplanned leaves"},
{"_id":"65f19252ecd2b756fab896bc","type":"initiative","aId":"I001","aName":"Goals"},{"_id":"65f19252ecd2b756fab896be","type":"initiative","aId":"I003","aName":"RFP's"}]
4. POST method to get reports by filtering from Date and toDate. Mandatory is empId in the example data which we post in the body to get minimum data of the previous 90 days.
If fromDate and toDate is not given we get only previous 90 days activities.
https://nisumscorecardservertesting.netlify.app/.netlify/functions/api/getActivities
Data In body Example :
/*Example post data
{
"empId":41689,
"fromDate":"2024-03-10",
"toDate":"2024-03-14"
}
API's for Deployment Team Usage
GET API's
1. To get all employees data
https://nisumscorecardserverdev.netlify.app/.netlify/functions/api/employees
2. To get individual employee data
https://nisumscorecardserverdev.netlify.app/.netlify/functions/api/employee/:id
3. To get master data of activities with types
https://nisumscorecardserverdev.netlify.app/.netlify/functions/api/activities
POST API's
1. POST api to validate login into dashboard
https://nisumscorecardserverdev.netlify.app/.netlify/functions/api/login
Data In body
example: {"empId":41689}
2. POST api to get reportees data under the employee who logged in. The data we send in the body should contain at least reportees filed with an array of employee empId, remaining all fields are optional.
https://nisumscorecardserverdev.netlify.app/.netlify/functions/api/getreportees
Data In body Example :
{
"reportees":[41689,41716,41710,41750,41751,41714],
"sort":{"type":"empId","order":-1}
,"page":1,"perPage":10,
"searchText":"eng"
}*/
3. POST method to add activity added by manager to his reportees. Id should contain all the fields given below in the example or else it throws an error.
Note: Users can add a custom aName field , from frontend a unique id will be generated and added as aID.
https://nisumscorecardserverdev.netlify.app/.netlify/functions/api/createActivity
Data In body Example :
//Example of post Data
/*
{
"empId":41689,
"data":{
"aName":"Approval of timesheet",
"aId":"D001",
"type":"duties",
"score":3,
"comments":""
}
}
Actual Activities Data this to be used when trying to create a activity to a employee:
[{"_id":"65f19252ecd2b756fab896b8","type":"duties","aId":"D001","aName":"Submission timesheet"},
{"_id":"65f19252ecd2b756fab896b9","type":"duties","aId":"D002","aName":"Successful deliveries"},
{"_id":"65f19252ecd2b756fab896bb","type":"duties","aId":"D004","aName":"Approval of timesheet"},
{"_id":"65f19252ecd2b756fab896bd","type":"initiative","aId":"I002","aName":"POC's"},
{"_id":"65f19252ecd2b756fab896ba","type":"duties","aId":"D003","aName":"Vacation/Unplanned leaves"},
{"_id":"65f19252ecd2b756fab896bc","type":"initiative","aId":"I001","aName":"Goals"},{"_id":"65f19252ecd2b756fab896be","type":"initiative","aId":"I003","aName":"RFP's"}]
4. POST method to get reports by filtering from Date and toDate. Mandatory is empId in the example data which we post in the body to get minimum data of the previous 90 days.
If fromDate and toDate is not given we get only previous 90 days activities.
https://nisumscorecardserverdev.netlify.app/.netlify/functions/api/getActivities
Data In body Example :
/*Example post data
{
"empId":41689,
"fromDate":"2024-03-10",
"toDate":"2024-03-14"
}
......@@ -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", "") || !_.get(data,"comments","")) {
if (!_.get(data, "aName", "") || !_.get(data, "aId", "") || !_.get(data, "type", "") || !_.get(data, "score", "") || !_.get(data,"comments","") ||!_.get(data,"ratedBy","") ) {
res.status(401).json({ "error": "Invalid Activity data" });
return;
}
......
......@@ -40,10 +40,11 @@ function Accordion({ title, data ,handleAddActivity,open,handleAccordian}) {
handleAccordian(title)
}
const headers = [
{ title: "Activity Name", id: "aName", width: "30%" },
{ title: "Activity Name", id: "aName", width: "25%" },
{ title: "Date", id: "recorded_date", width: "20%", render: (value) => moment(value).format('DD-MM-YYYY') },
{title: "Rated By", id: "ratedBy", width: "25%"},
{ title: "Score", id: "score", width: "10%", render: (value) => <div className="w-[35px] bg-blue-400 rounded-full text-white font-bold text-center p-[4px]">{value}</div> },
{ title: "Comments", id: "comments", width: "40%" },
{ title: "Comments", id: "comments", width: "30%" },
];
if(loading && title =="Duties")return <Loading/>
......@@ -55,7 +56,7 @@ function Accordion({ title, data ,handleAddActivity,open,handleAccordian}) {
<button
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" >
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 hover:bg-gray-100 gap-3" data-accordion-target="#accordion-collapse-body-2" aria-expanded="false" aria-controls="accordion-collapse-body-2" >
<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}
......@@ -75,7 +76,7 @@ function Accordion({ title, data ,handleAddActivity,open,handleAccordian}) {
className={`${!open && "hidden"} mt-2`}
aria-labelledby="accordion-collapse-heading-2"
>
<Table headers={headers} loading={loading} data={data} maxHeight={10}/>
<Table headers={headers} loading={loading} data={data} />
</div>
</div>
);
......
......@@ -21,7 +21,7 @@ function Header({isOpen}) {
return (
<div className="flex items-center justify-between py-5 px-10" onClick={() => setOpen(!open)}>
<img src="/logo.png"/>
<Link to={"/dashboard"}><img src="/logo.png"/></Link>
<div className="flex items-center relative">
<button className=" -mt-1 text-2xl flex" onClick={() => setOpen(!open)}>
<img src="/user.png" width="35px" height="35px" className="mt-2 pr-2" />
......
......@@ -21,7 +21,7 @@ function LeftSidebar() {
{reportees?.map(({ empName, score, empId }) => (
<Link
to={`/viewreportee/${empId}`}
className={`flex items-center hover:bg-blue-400 hover:text-white hover:rounded-2xl 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 ? "blue-400 text-white rounded-2xl" : "white"
} p-2 justify-between mb-1 w-full`}
key={empId}
>
......
......@@ -8,8 +8,6 @@ const Loading = memo(() => {
<div className='w-100'>
<p className='text-blue-500 flex justify-center items-center h-full mt-28'>
<img src={Spin} alt="Loading" width={100} height={100} />
{/* loading */}
</p>
</div>
);
......
import axios from "axios";
import React, { useMemo, useEffect, useState, useCallback } from "react";
import { useSelector } from "react-redux";
import { base_url } from "../../utils/constants";
import { v4 as uuidv4 } from 'uuid';
import Loading from "../loading Component/Loading";
// import Loading from "../loading Component/Loading";
export default function MyModal({ visible, onClose, type, handleAddActivity }) {
const {user} = useSelector((state) => state.userDetails)
const [activitiesList, setActivitiesList] = useState([])
const [enableSubmit, setEnableSubmit] = useState(false)
const [activityData, setActivityData] = useState({ aName: "", aId: "", type: type, score: 0, comments: "" })
const [activityData, setActivityData] = useState({ aName: "",ratedBy:"", aId: "", type: type, score: 0, comments: "" })
const [activityType, setActivtyType] = useState("")
const [showCustActivity, setShowActivity] = useState(false);
const [modalLoading, setModalLoading] = useState(true)
......@@ -79,8 +81,9 @@ export default function MyModal({ visible, onClose, type, handleAddActivity }) {
SentenceCase(type)
if (visible === false) {
setActivityData({ aName: "", aId: "", type: type, score: 0, comments: "" })
setActivityData({ aName: "",ratedBy:"", aId: "", type: type, score: 0, comments: "" })
} else {
setActivityData({...activityData,ratedBy:user.empName})
getActivitysList(type);
setModalLoading(true)
}
......@@ -107,7 +110,7 @@ export default function MyModal({ visible, onClose, type, handleAddActivity }) {
<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<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}>
<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 " 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>)
......@@ -116,14 +119,14 @@ export default function MyModal({ visible, onClose, type, handleAddActivity }) {
<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<span className="text-[15px]">*</span>:</label>
<label className={`font-medium 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<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)} />
<label htmlFor="appreciate" className=" font-medium ">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 " 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>
<div className={`flex ${!showScore && 'hidden'}`}>
<span>SCORE<span className="text-[15px]">*</span>: </span>
......@@ -135,8 +138,8 @@ export default function MyModal({ visible, onClose, type, handleAddActivity }) {
</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<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)}
<label htmlFor="comments" className="block w-3/12 mb-20 text-start font-medium ">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 " placeholder="Comments" onChange={(e) => handleComments(e)}
onClick={(e) => e.stopPropagation()}
onKeyDown={(e) => {
if (e.key === " ") {
......
......@@ -14,7 +14,7 @@ function Sidebar() {
<ul className="space-y-1.5">
<li>
<Link
className={`flex items-center gap-x-3.5 py-2 px-2.5 bg-gray-100 text-sm text-slate-700 rounded-lg hover:bg-gray-100 dark:bg-gray-900 dark:text-white dark:focus:outline-none dark:focus:ring-1 dark:focus:ring-gray-600`}
className={`flex items-center gap-x-3.5 py-2 px-2.5 bg-gray-100 text-sm text-slate-700 rounded-lg hover:bg-gray-100 `}
to={`/dashboard`}
>
<svg
......@@ -37,7 +37,7 @@ function Sidebar() {
</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 dark:hover:bg-gray-900 dark:text-slate-400 dark:hover:text-slate-300 dark:focus:outline-none dark:focus:ring-1 dark:focus:ring-gray-600`}
className={`flex items-center gap-x-3.5 py-2 px-2.5 text-sm text-slate-700 rounded-lg hover:bg-gray-100 `}
>
<svg
className="size-4"
......
......@@ -6,8 +6,8 @@ function Table({headers, data,loading, maxHeight}) {
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">
<table className="w-full text-sm text-left rtl:text-right text-gray-500 bg-transparent justify-center border-separate border-spacing-y-2">
<thead className="text-xs text-gray-700 uppercase bg-gray-50">
<tr className="mb-2">
{headers?.map((item,index) => (
<th key={index} scope="col" className={`px-6 py-4 w-[${item.width}]`} >
......@@ -17,13 +17,15 @@ function Table({headers, data,loading, maxHeight}) {
</tr>
</thead>
{
(data?.length)?<tbody>
(data?.length)?<tbody >
{
data?.map((item, index) => (
<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">
<tr key={item.id} className="bg-white hover:bg-gray-300 " >
{
headers?.map(({render, id}) => (
<td key={`${item.id}_${id}`} 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-2 " >
<span title={(id=="comments")?item[id]:null} className="listData">{render ? render(item[id]) : item[id] === "" ? "NA" : item[id] }</span>
</td>
))
}
</tr>
......
......@@ -6,11 +6,22 @@ code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
/* .listData:nth-child(4) {
padding-left: 35px;
} */
.active{
background: rgb(96 165 250);
color:white;
border-radius:50%
}
.listData {
white-space: nowrap;
width: 100px;
overflow: hidden;
text-overflow: ellipsis;
display: inline-block;
}
......@@ -48,38 +48,47 @@ function Home() {
<div className="container py-10 px-10 mx-0 min-w-full h-screen flex items-center justify-center bg-blue-100 ">
<div className="">
<h1 className="text-4xl font-extrabold leading-none tracking-tight md:text-5xl lg:text-6xl text-purple-900 mb-10 ">SCORE CARD</h1>
{
(loading)?<Loading/>:<div className="max-w-sm p-10 bg-white border border-gray-400 rounded-lg shadow dark:bg-gray-800 dark:border-gray-700 ">
{/* {
(loading)?<Loading/>: */}
<div className="max-w-sm p-10 bg-white border border-gray-400 rounded-lg shadow ">
<label
htmlFor="email"
className="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
className="block mb-2 text-sm font-medium text-gray-900 "
>
Employee Id
</label>
<input
type="text"
id="email"
className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full 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"
placeholder=""
className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 "
placeholder="Enter Employee ID"
required
onChange={(e) => setId(e.target.value)}
/>
<div className="dark:text-white text-red-600">
{
}
<div className=" text-red-600">
{
errorMsg!==""? <span>{errorMsg}</span>:null
}
</div>
<div className="flex justify-between">
<button
className="bg-purple-900 text-white disabled:bg-purple-900 hover:bg-blue-400 font-bold py-2 px-4 mt-6 rounded text-center ml-15"
className="bg-purple-900 text-white disabled:bg-purple-900 hover:bg-blue-400 font-bold py-2 px-4 mt-6 rounded text-center"
onClick={handleNavigate}
disabled={!id}
>
Submit
</button>
</div>
{
(loading)?<img src="/Loader2.gif" className="mt-2" width={100} height={100}/>:null
}
</div>
</div>
{/* } */}
</div>
</div>
);
}
......
......@@ -49,10 +49,6 @@ function Reports() {
}
}
// const getReports = (startDate = null, endDate = null) => {
// const data = { "empId": empId, "fromDate": startDate, "toDate": endDate }
// dispatch(fetchReports(data))
// }
const fetchLatestReporteesData = () => {
if (user) {
const data = {
......@@ -75,8 +71,6 @@ function Reports() {
await axios.post(`${base_url}/createActivity`, newData)
.then(async (result) => {
fetchLatestReporteesData()
// getReports()
})
} else {
alert("Please login")
......@@ -102,6 +96,7 @@ function Reports() {
useEffect(() => {
if (user) {
navigate(`/viewreportee/${id}`)
setOpen({ "accordianOne": false, "accordianTwo": false })
} else {
navigate("/")
}
......
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