-
Notifications
You must be signed in to change notification settings - Fork 22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
FEATURE/HCMPRE-1806 : Attendance register #2115
base: console
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,7 +12,7 @@ | |
<title>DIGIT</title> | ||
<link rel="stylesheet" href="https://unpkg.com/@egovernments/[email protected]/dist/index.css" /> | ||
<link rel="stylesheet" href="https://unpkg.com/@egovernments/[email protected]/dist/index.css" /> | ||
<link rel="stylesheet" href="https://unpkg.com/@egovernments/[email protected].34/dist/index.css" /> | ||
<link rel="stylesheet" href="https://unpkg.com/@egovernments/[email protected].35/dist/index.css" /> | ||
|
||
|
||
<!-- added below css for hcm-workbench module inclusion--> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
{ | ||
"name": "@egovernments/digit-ui-health-css", | ||
"version": "0.2.34", | ||
"version": "0.2.35", | ||
"license": "MIT", | ||
"main": "dist/index.css", | ||
"author": "Jagankumar <[email protected]>", | ||
|
@@ -66,4 +66,4 @@ | |
"digit-ui", | ||
"css" | ||
] | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -231,4 +231,61 @@ | |
> div:nth-of-type(1) { | ||
width: 69%; | ||
} | ||
} | ||
} | ||
|
||
.attendanceCell { | ||
display: flex; | ||
} | ||
|
||
.chip-container { | ||
display: flex; | ||
align-items: center; | ||
gap: 0.5rem; /* Adjust the spacing between chips and the button */ | ||
} | ||
|
||
.chip-wrapper { | ||
display: inline-flex; | ||
} | ||
|
||
.more-button { | ||
margin-left: 0.5rem; /* Adjust the spacing between chips and the + button */ | ||
} | ||
|
||
/* Container for the progress bar */ | ||
.progress-bar-container { | ||
position: relative; | ||
width: 20rem; | ||
max-width: 20rem; | ||
height: 1.5rem; | ||
background-color: #f3f3f3; | ||
border-radius: 1rem; | ||
overflow: hidden; | ||
border: 1px solid #d6d5d4; | ||
} | ||
|
||
/* The fill of the progress bar */ | ||
.progress-bar-fill { | ||
height: 100%; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) Use theme variables for color consistency Replace the hardcoded color with a theme variable to maintain consistency with the design system. - background-color: #2BD27F; /* Use your PRIMARY_COLOR */
+ background-color: theme(digitv2.lightTheme.success);
|
||
background-color: #2BD27F; /* Use your PRIMARY_COLOR */ | ||
transition: width 0.4s ease-in-out; | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
} | ||
|
||
/* Text inside the progress bar */ | ||
.progress-bar-text { | ||
position: absolute; | ||
width: 100%; | ||
text-align: center; | ||
font-size: 1rem; | ||
color: #fff; | ||
font-weight: bold; | ||
pointer-events: none; | ||
} | ||
|
||
.assign-users-popup { | ||
.digit-popup-heading{ | ||
font-family: 'Roboto Condensed', sans-serif !important; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,257 @@ | ||
import React, { useState, useCallback, useEffect } from 'react'; | ||
import { Card, Button, PopUp, Loader, Chip, TextInput } from "@egovernments/digit-ui-components"; | ||
import DataTable from "react-data-table-component"; | ||
import { CustomSVG } from "@egovernments/digit-ui-components"; | ||
import { tableCustomStyle } from "./tableCustomStyle"; | ||
import { useTranslation } from "react-i18next"; | ||
import ProgressBar from './ProgressBar'; | ||
|
||
const SupervisorPopup = ({ setShowPopUp, supervisors }) => { | ||
const { t } = useTranslation(); | ||
return ( | ||
<PopUp | ||
className="wrapper-popup-boundary-chips" | ||
style={{ maxWidth: "40%" }} | ||
type="default" | ||
heading={t("REGISTER_SUPERVISORS_ASSIGNED")} | ||
footerChildren={[]} | ||
onOverlayClick={() => setShowPopUp(false)} | ||
onClose={() => setShowPopUp(false)} | ||
> | ||
<div className="digit-tag-container userAccessCell" | ||
style={{ | ||
display: "flex", | ||
flexWrap: "wrap", | ||
justifyContent: "flex-start", | ||
gap: "8px" | ||
}}> | ||
{supervisors.map((supervisor, index) => ( | ||
<Chip | ||
key={index} | ||
text={supervisor} | ||
hideClose={true} | ||
/> | ||
))} | ||
</div> | ||
</PopUp> | ||
); | ||
}; | ||
|
||
const IndividualUserTable = ({ tenantId, staffAttendeeIds = [], supervisorName = "" }) => { | ||
const { t } = useTranslation(); | ||
const [totalRows, setTotalRows] = useState(0); | ||
const [currentPage, setCurrentPage] = useState(1); | ||
const [rowsPerPage, setRowsPerPage] = useState(5); | ||
const [searchQuery, setSearchQuery] = useState(''); | ||
const [paginatedData, setPaginatedData] = useState([]); | ||
const [chipPopUpRowId, setChipPopUpRowId] = useState(null); | ||
|
||
const individualUsers = { | ||
url: `/health-individual/v1/_search`, | ||
params: { | ||
tenantId: tenantId, | ||
limit: 1000, | ||
offset: 0 | ||
}, | ||
body: { | ||
Individual: {}, | ||
}, | ||
}; | ||
|
||
const { isLoading, data, isFetching } = Digit.Hooks.useCustomAPIHook(individualUsers); | ||
|
||
const getDisplayData = useCallback(() => { | ||
if (!data?.Individual) return []; | ||
|
||
const filteredData = data.Individual.filter(user => { | ||
const userName = user.name?.givenName?.toLowerCase() || ''; | ||
return userName.includes(searchQuery.toLowerCase()); | ||
}).map(user => ({ | ||
...user, | ||
isAssigned: staffAttendeeIds.includes(user.id) | ||
})); | ||
|
||
setTotalRows(filteredData.length); | ||
|
||
const startIndex = (currentPage - 1) * rowsPerPage; | ||
const endIndex = startIndex + rowsPerPage; | ||
return filteredData.slice(startIndex, endIndex); | ||
}, [data, searchQuery, staffAttendeeIds, currentPage, rowsPerPage]); | ||
|
||
useEffect(() => { | ||
if (data?.Individual) { | ||
setPaginatedData(getDisplayData()); | ||
} | ||
}, [data, searchQuery, currentPage, rowsPerPage, getDisplayData]); | ||
|
||
const handlePaginationChange = (page) => { | ||
setCurrentPage(page); | ||
}; | ||
|
||
const handleRowsPerPageChange = (newPerPage) => { | ||
setRowsPerPage(newPerPage); | ||
setCurrentPage(1); | ||
}; | ||
|
||
const handleSearchChange = (e) => { | ||
setSearchQuery(e.target.value); | ||
setCurrentPage(1); | ||
}; | ||
|
||
const handleAssign = (userId) => { | ||
//code to be written | ||
}; | ||
|
||
const handleUnassign = (userId) => { | ||
//code to be written | ||
}; | ||
Comment on lines
+101
to
+107
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) Implement the placeholder functions. The Would you like me to help implement these functions with proper error handling and state updates? |
||
|
||
const columns = [ | ||
{ | ||
name: t('REGISTER_NAME'), | ||
selector: row => row.name?.givenName || '', | ||
sortable: true, | ||
}, | ||
{ | ||
name: t('REGISTER_MOBILE_NUMBER'), | ||
selector: row => row.mobileNumber || '', | ||
sortable: true, | ||
}, | ||
{ | ||
name: t("REGISTER_USER_TYPE"), | ||
selector: row => row?.userDetails?.type || '', | ||
sortable: true | ||
}, | ||
{ | ||
name: t('REGISTER_ASSIGNED_SUPERVISOR'), | ||
selector: row => row.isAssigned ? supervisorName : t('REGISTER_SUPERVISOR_NOT_ASSIGNED'), | ||
sortable: true, | ||
grow: 2, | ||
cell: row => { | ||
const supervisors = row.isAssigned ? [supervisorName] : []; | ||
|
||
return ( | ||
<div className="digit-tag-container"> | ||
{supervisors.length > 0 ? ( | ||
<div className="chip-container"> | ||
{supervisors.slice(0, 2).map((supervisor, index) => ( | ||
<div key={index} className="chip-wrapper"> | ||
<Chip | ||
text={supervisor} | ||
hideClose={true} | ||
/> | ||
</div> | ||
))} | ||
|
||
{supervisors.length > 2 && ( | ||
<Button | ||
label={`+${supervisors.length - 2} ${t("ES_MORE")}`} | ||
variation="link" | ||
onClick={() => setChipPopUpRowId(row.id)} | ||
className="more-button" | ||
style={{ | ||
height: "2rem", | ||
minWidth: "4.188rem", | ||
minHeight: "2rem", | ||
padding: "0.5rem" | ||
}} | ||
textStyles={{ | ||
fontSize: "0.875rem", | ||
fontWeight: "400", | ||
color: "#C84C0E", | ||
}} | ||
/> | ||
)} | ||
|
||
{chipPopUpRowId === row.id && ( | ||
<SupervisorPopup | ||
setShowPopUp={setChipPopUpRowId} | ||
supervisors={supervisors} | ||
/> | ||
)} | ||
</div> | ||
) : ( | ||
<span className="text-gray-400">{t('REGISTER_SUPERVISOR_NOT_ASSIGNED')}</span> | ||
)} | ||
</div> | ||
); | ||
}, | ||
}, | ||
{ | ||
name: t('REGISTER_ACTIONS'), | ||
cell: row => ( | ||
<div> | ||
{row.isAssigned ? ( | ||
<Button | ||
label={t('REGISTER_UNASSIGN')} | ||
variation="secondary" | ||
icon="Close" | ||
onClick={() => handleUnassign(row.id)} | ||
/> | ||
) : ( | ||
<Button | ||
label={t('REGISTER_ASSIGN')} | ||
variation="primary" | ||
icon="CheckCircle" | ||
onClick={() => handleAssign(row.id)} | ||
/> | ||
)} | ||
</div> | ||
) | ||
}, | ||
]; | ||
|
||
return ( | ||
<div className="w-full"> | ||
{isLoading && <Loader />} | ||
{!isLoading && ( | ||
<div> | ||
<Card type={"secondary"} style={{ maxWidth: "100%", overflow: "auto", marginBottom: "1rem" }}> | ||
<div> | ||
<div style={{ fontFamily: "Roboto Condensed", marginBottom: "0.5rem" }}>{t("REGISTER_USER_NAME")}</div> | ||
<TextInput | ||
style={{ maxWidth: "fit-content" }} | ||
disabled={false} | ||
className="textinput-example" | ||
type="text" | ||
name={t("REGISTER_USER_NAME")} | ||
value={searchQuery} | ||
onChange={handleSearchChange} | ||
placeholder={t("SEARCH_BY_NAME")} | ||
/> | ||
</div> | ||
</Card> | ||
|
||
<DataTable | ||
columns={columns} | ||
data={paginatedData} | ||
progressPending={isLoading || isFetching} | ||
progressComponent={<Loader />} | ||
pagination | ||
paginationServer | ||
customStyles={tableCustomStyle} | ||
paginationTotalRows={totalRows} | ||
onChangePage={handlePaginationChange} | ||
onChangeRowsPerPage={handleRowsPerPageChange} | ||
paginationPerPage={rowsPerPage} | ||
sortIcon={<CustomSVG.SortUp width="16px" height="16px" fill="#0b4b66" />} | ||
paginationRowsPerPageOptions={[5, 10, 15, 20]} | ||
noDataComponent={<div>{t('REGISTER_NO_RECORDS_FOUND')}</div>} | ||
/> | ||
|
||
{/* Progress Bar Section */} | ||
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginTop: "1rem" }}> | ||
<div style={{ display: "flex", alignItems: "center", gap: "1rem" }}> | ||
<span style={{ fontFamily: "Roboto Condensed", fontWeight: 600 }}>{t("REGISTER_ASSIGNED_USERS")}</span> | ||
<ProgressBar amount={40} total={100} /> | ||
<span style={{ fontWeight: 600 }}>{`${40}%`}</span> | ||
</div> | ||
Comment on lines
+246
to
+248
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix hardcoded progress values. The progress bar values (40 and 100) are hardcoded. These should be calculated based on actual data. Apply this diff to fix the issue: - <ProgressBar amount={40} total={100} />
- <span style={{ fontWeight: 600 }}>{`${40}%`}</span>
+ <ProgressBar amount={staffAttendeeIds.length} total={data?.Individual?.length || 0} />
+ <span style={{ fontWeight: 600 }}>{`${data?.Individual?.length ? Math.round((staffAttendeeIds.length / data.Individual.length) * 100) : 0}%`}</span>
|
||
</div> | ||
</div> | ||
)} | ||
</div> | ||
); | ||
|
||
}; | ||
|
||
export default IndividualUserTable; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import React from 'react'; | ||
|
||
const ProgressBar = ({ amount, total }) => { | ||
const percentage = Math.min((amount / total) * 100, 100); | ||
suryansh-egov marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Handle potential division by zero in percentage calculation In the calculation of const percentage = Math.min((amount / total) * 100, 100); If Apply this diff to handle division by zero: - const percentage = Math.min((amount / total) * 100, 100);
+ const percentage = total ? Math.min((amount / total) * 100, 100) : 0; |
||
|
||
return ( | ||
<div className="progress-bar-container"> | ||
<div className="progress-bar-fill" style={{ width: `${percentage}%` }}> | ||
<span className="progress-bar-text">{`${percentage.toFixed(1)}%`}</span> | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default ProgressBar; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick (assertive)
Consider improving chip container accessibility and responsiveness
The attendance cell and chip container implementation could benefit from some improvements:
📝 Committable suggestion