Skip to content

PM- 1502 Scorecards filtering and listing UI #1184

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

Open
wants to merge 15 commits into
base: feat/review
Choose a base branch
from

Conversation

himaniraghav3
Copy link
Collaborator

Related JIRA Ticket:

https://topcoder.atlassian.net/browse/PM-1502
https://topcoder.atlassian.net/browse/PM-1505

What's in this PR?

Scorecards listing table and filtering UI
local topcoder-dev com_review_scorecard

@@ -6,18 +6,49 @@
justify-content: flex-end;
align-items: center;
padding: 16px 0;
gap: $sp-4;
gap: $sp-2;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider verifying if changing the gap from $sp-4 to $sp-2 aligns with the overall design consistency and spacing guidelines of the application.

padding: 7px 11px;

&:disabled {
background-color: #E9ECEF !important;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use of !important can lead to specificity issues. Consider refactoring the CSS to avoid using !important if possible.

@@ -35,4 +66,8 @@
@media (max-width: #{$mobile-max}) {
justify-content: center;
}

:global(.btn-style-secondary) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid using !important as it can make the CSS harder to maintain and override. Consider refactoring the styles to achieve the desired effect without using !important.

@@ -84,7 +83,6 @@ const Pagination: FC<PaginationProps> = (props: PaginationProps) => {
size='md'
icon={IconOutline.ChevronLeftIcon}
iconToLeft
label='PREVIOUS'
disabled={props.page === 1 || props.disabled}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The label prop has been removed from the Button component. Ensure that this change is intentional and that the button still meets accessibility requirements, as removing text labels can affect screen readers.

@@ -93,7 +91,6 @@ const Pagination: FC<PaginationProps> = (props: PaginationProps) => {
<Button
key={`page-${i}`}
secondary
variant='round'
label={`${i}`}
onClick={createHandlePageClick(i)}
className={i === props.page ? styles.active : ''}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variant='round' prop has been removed from the Button component. If this was intentional, ensure that the design requirements do not require the buttons to have a round variant. If it was removed by mistake, consider adding it back to maintain the intended button style.

@@ -107,7 +104,6 @@ const Pagination: FC<PaginationProps> = (props: PaginationProps) => {
size='md'

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The label='NEXT' prop has been removed from the Button component. Ensure that this change is intentional and that the button's functionality and accessibility are not affected by the removal of the label.

@@ -118,7 +114,6 @@ const Pagination: FC<PaginationProps> = (props: PaginationProps) => {
size='md'

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The label='LAST' prop was removed from the Button component. Ensure that this change is intentional and that the UI/UX is not negatively impacted by the absence of this label.

import { ReviewAppContext } from '../../contexts'
import { ReviewAppContextModel } from '../../models'

import { getTabIdFromPathName, getTabsConfig } from './config'

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function getTabsConfig is imported but not used in this file. Consider removing the import if it is not needed.

const activeTabPathName: string = useMemo<string>(
() => getTabIdFromPathName(pathname),
[pathname],
() => getTabIdFromPathName(pathname, userRoles),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function getTabIdFromPathName now takes an additional parameter userRoles. Ensure that this function is designed to handle the new parameter correctly and that it doesn't introduce any unintended side effects.

)
const [activeTab, setActiveTab]: [
string,
Dispatch<SetStateAction<string>>
] = useState<string>(activeTabPathName)

useEffect(() => {
setActiveTab(activeTabPathName)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The useEffect hook is used to update activeTab whenever activeTabPathName changes. Verify that this does not cause unnecessary re-renders or side effects, especially if activeTabPathName changes frequently.

@@ -58,7 +72,7 @@ const NavTabs: FC = () => {
Review
</div>
<ul className={styles.tab}>
{TabsConfig.map(tab => (
{tabs.map(tab => (

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable tabs is used here, but it is not clear from the diff if it has been defined or imported correctly. Ensure that tabs is properly defined or imported in the component to avoid runtime errors.

@@ -5,25 +5,37 @@ import {
activeReviewAssigmentsRouteId,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable name activeReviewAssigmentsRouteId seems to have a typo. It should be activeReviewAssignmentsRouteId instead of activeReviewAssigmentsRouteId. Please correct the spelling.


export function getTabIdFromPathName(pathname: string): string {
const matchItem = _.find(TabsConfig, item => pathname.includes(`/${item.id}`))
if (userRoles.includes('administrator')) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider checking for null or undefined values for userRoles before calling includes. This will prevent potential runtime errors if userRoles is not properly initialized.

padding: 20px;
background-color: #E0E4E84D;
justify-content: center;
align-items: center;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The property align-items: center; is duplicated on line 8. Consider removing one of them to avoid redundancy.

justify-content: center;
align-items: center;

.inputWrapper {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The .inputWrapper class has gap: $sp-1;, but $sp-1 is not defined in the provided code. Ensure that $sp-1 is defined in the imported styles or replace it with a valid value.

}

.inputIcon {
color: $black-60;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The .inputIcon class uses $black-60, but this variable is not defined in the provided code. Ensure that $black-60 is defined in the imported styles or replace it with a valid value.

font-weight: 700;
}

@media (max-width: 768px) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The media query for max-width 768px changes the flex direction to column, which might affect the layout significantly. Ensure that this change is intended and does not disrupt the UI design.

status: '',
type: '',
})
}, [])

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding props as a dependency to the useCallback hook to ensure that the latest props.onFiltersChange function is used when the component re-renders.

}

.tableBreakCell {
white-space: break-spaces !important;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using a more specific class name instead of !important to ensure styles are applied correctly without overriding other styles. This can help maintain better control over CSS specificity.

.paginationText {
font-size: 12px;
font-weight: 400;
color: #0A0A0A;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The color #0A0A0A is hardcoded. Consider using a variable from your color palette for consistency and easier maintenance.

import styles from './TableScorecards.module.scss'

interface Props {
className?: string

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using a more specific type instead of any for metadata to improve type safety and clarity.

<PencilIcon />
<span>Edit</span>
</div>
<div className={styles.actionItem} onClick={bind(props.onClone, this, data)}>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use of bind here might be unnecessary. Consider using an arrow function instead to maintain the correct this context: () => props.onClone(data).

{' '}
of
{' '}
{props.metadata.total}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accessing props.metadata.total directly without checking if metadata is defined could lead to runtime errors. Consider adding a null check or default value.


export interface ScorecardsResponse {
scoreCards: Scorecard[]
metadata: any

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider specifying a more precise type for metadata instead of using any to improve type safety and maintainability.

export interface ScorecardsResponse {
scoreCards: Scorecard[]
metadata: any
error?: any

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider specifying a more precise type for error instead of using any to improve type safety and maintainability.

...(status ? { status } : {}),
})

const fetcher = (url: string): Promise<ScorecardsResponse> => xhrGetAsync<ScorecardsResponse>(url)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider handling potential errors from xhrGetAsync more explicitly to ensure robust error management.

[ProjectType.QUALITY_ASSURANCE]: 'Quality Assurance',
}

export const categoryByProjectType = {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using Record<ProjectType, string[]> for categoryByProjectType to ensure type safety and consistency with other similar structures.

'Reporting',
'Automated Testing',
],
} satisfies Record<ProjectType, string[]>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The satisfies keyword is used here, but it might be more straightforward to use Record<ProjectType, string[]> directly in the type declaration for clarity and consistency.

@@ -1,12 +1,26 @@
/**
* Scorecards service
*/
import { xhrPostAsync } from '~/libs/core'

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The import statement for xhrPostAsync should be organized alphabetically with the other import statements for consistency.

import { adjustScorecardInfo, ScorecardInfo } from '../models'
import { adjustScorecardInfo, Scorecard, ScorecardInfo } from '../models'

const baseUrl = `${EnvironmentConfig.API.V6}/review/scorecards`

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using const for baseUrl only if it is not intended to be reassigned. Otherwise, use let.

* @returns resolves to the cloned scorecard info
*/
export const cloneScorecard = async (scorecard: Pick<Scorecard, 'id'>): Promise<Scorecard> => (
xhrPostAsync(`${baseUrl}/${scorecard.id}/clone`, {})

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding error handling for the xhrPostAsync call to manage potential failures during the cloning process. This will help ensure that any issues are properly caught and handled.

* The router outlet.
*/

import { FC, PropsWithChildren, useContext, useEffect, useMemo } from 'react'

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The import statement for Routes is missing from the react-router-dom package. Ensure that Routes is correctly imported to avoid runtime errors.

return (
<>
<Outlet />
<Routes>{childRoutes}</Routes>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Routes component should wrap Route components. Ensure that childRoutes returns valid Route elements to avoid rendering issues.

function useChildRoutes(): Array<JSX.Element> | undefined {
const { getRouteElement }: RouterContextData = useContext(routerContext)
const childRoutes = useMemo(
() => reviewRoutes[0].children

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding a null check for reviewRoutes[0] to prevent potential runtime errors if reviewRoutes is empty or undefined.

const { getRouteElement }: RouterContextData = useContext(routerContext)
const childRoutes = useMemo(
() => reviewRoutes[0].children
?.find(r => r.id === scorecardRouteId)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The optional chaining operator ?. is used, but ensure that reviewRoutes[0].children is always defined to avoid unexpected behavior.

@@ -0,0 +1,3 @@
.totalScorecards {
margin-top: 10px;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a good practice to ensure that files end with a newline character to avoid potential issues with some tools and version control systems.


import styles from './ScorecardsListPage.module.scss'

export const ScorecardsListPage: FC<{}> = () => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider specifying the type of the props object in the component definition for better type safety and clarity.

const confirmation = useConfirmationModal()

const [filters, setFilters] = useState({
category: '',

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using a more descriptive name for the filter keys, such as 'categoryFilter', 'nameFilter', etc., to improve code readability.

scoreCards: scorecards,
metadata,
isValidating: isLoadingScorecards,
}: ScorecardsResponse = useFetchScorecards({

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider handling potential errors from the useFetchScorecards hook to ensure the application can gracefully handle API failures.

totalPages={metadata?.totalPages}
page={page}
setPage={setPage}
datas={scorecards.map((item, i) => ({

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The datas prop name is unconventional. Consider renaming it to data for consistency and clarity.

{
element: <ScorecardsListPage />,
id: 'list-scorecards-page',
route: '',

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider providing a more descriptive route name instead of an empty string for better clarity and maintainability.

@@ -29,6 +29,7 @@ const PaymentProviderTable: React.FC<PaymentMethodTableProps> = (props: PaymentM
<th className='body-ultra-small-bold'>CONNECTED PROVIDER</th>
<th className='body-ultra-small-bold'>PROVIDER ID</th>
<th className='body-ultra-small-bold'>STATUS</th>
{/* eslint-disable-next-line jsx-a11y/control-has-associated-label */}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Disabling ESLint rules should be avoided unless absolutely necessary. Consider addressing the accessibility issue by ensuring that the control has an associated label, which will improve accessibility compliance.

@@ -30,6 +30,7 @@ const TaxFormTable: React.FC<TaxFormTableProps> = (props: TaxFormTableProps) =>
<th className='body-ultra-small-bold'>FORM</th>
<th className='body-ultra-small-bold'>DATE FILED</th>
<th className='body-ultra-small-bold'>STATUS</th>
{/* eslint-disable-next-line jsx-a11y/control-has-associated-label */}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Disabling eslint rules should be avoided unless absolutely necessary. Consider addressing the accessibility issue by ensuring that the control has an associated label, which will improve accessibility compliance.

}

@media (max-width: 768px) {
.filtersContainer {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding a comment to explain the purpose of the flex-wrap: wrap; rule for .filtersContainer to improve readability.

.categorySelect,
.statusSelect,
.clearButton {
flex: 1 1 calc(50% - 0.5rem);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment // Two filters per row should be removed as it violates the instruction to never suggest adding comments to the code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants