Skip to content

Topcoder Admin App - Marathon Match Functionality #1092

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

Merged
merged 3 commits into from
Jun 4, 2025

Conversation

suppermancool
Copy link
Collaborator

@@ -145,6 +149,11 @@ export const adminRoutes: ReadonlyArray<PlatformRoute> = [
id: 'add-resource',
route: ':challengeId/manage-resource/add',
},
{
element: <ManageSubmissionPage />,
Copy link

Choose a reason for hiding this comment

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

The component <ManageSubmissionPage /> is added here, but ensure that it is imported correctly at the top of the file. If it's not imported, it will cause a runtime error.

Copy link
Collaborator

Choose a reason for hiding this comment

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

@suppermancool - Can you double check t his please?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@jmgasper this code is correct

}

export const ManageSubmissionPage: FC<Props> = (props: Props) => {
const { challengeId = '' }: { challengeId?: string } = useParams<{
Copy link

Choose a reason for hiding this comment

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

The destructuring of useParams could be simplified by directly using useParams<{ challengeId: string }>() without the need for an intermediate type annotation.

Copy link
Collaborator

Choose a reason for hiding this comment

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

@suppermancool - Can you double check this please?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@jmgasper, the intermediate type annotation is required by eslint without it the 'yarn lint' will fail

pageTitle='Submission Management'
className={classNames(styles.container, props.className)}
headerActions={(
<LinkButton primary light to='./../..' size='lg'>
Copy link

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 path for the LinkButton to improve readability and maintainability. The current path './../..' is not immediately clear and could be replaced with a named route or a clearer path string.

) : (
<div className={styles.blockTableContainer}>
<SubmissionTable
datas={submissions}
Copy link

Choose a reason for hiding this comment

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

The prop datas should be renamed to data to follow standard naming conventions and improve readability.

Copy link
Collaborator

Choose a reason for hiding this comment

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

@suppermancool - Can you fix please?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@jmgasper done in the latest commit

testType: string,
): RequestBusAPI => ({
'mime-type': 'application/json',
originator: 'MMFinalScoreProcessor',
Copy link

Choose a reason for hiding this comment

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

Consider using a constant or configuration setting for the originator value 'MMFinalScoreProcessor' to avoid hardcoding it directly in the function. This can improve maintainability and make it easier to update if the originator changes in the future.

},
timestamp: new Date()
.toISOString(),
topic: 'submission.notification.score',
Copy link

Choose a reason for hiding this comment

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

Consider using a constant or configuration setting for the topic value 'submission.notification.score' to avoid hardcoding it directly in the function. This can improve maintainability and make it easier to update if the topic changes in the future.

@@ -134,6 +135,7 @@ const Actions: FC<{
challenge: Challenge
currentFilters: ChallengeFilterCriteria
}> = props => {
const isMM = useMemo(() => checkIsMM(props.challenge), [props.challenge])
Copy link

Choose a reason for hiding this comment

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

Consider renaming the isMM variable to something more descriptive, such as isMarathonMatch, to improve code readability.

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

interface Props {
className?: string
Copy link

Choose a reason for hiding this comment

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

Consider renaming datas to submissions for better clarity and to follow common naming conventions.

...column,
className: '',
label: `${column.label as string} label`,
mobileType: 'label',
Copy link

Choose a reason for hiding this comment

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

The label ${column.label as string} label seems redundant. Consider simplifying it to just ${column.label as string}.

columns={columns}
data={props.datas}
removeDefaultSort
onToggleSort={_.noop}
Copy link

Choose a reason for hiding this comment

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

The removeDefaultSort prop is used here but not defined in the Table component props. Ensure that this prop is handled correctly in the Table component.

open: boolean
setOpen: Dispatch<SetStateAction<boolean>>
}) => {
const createToggle = () => (): void => triggerProps.setOpen(!triggerProps.open)
Copy link

Choose a reason for hiding this comment

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

Consider renaming createToggle to something more descriptive like toggleDropdown to better convey its purpose.

}) => {
const createToggle = () => (): void => triggerProps.setOpen(!triggerProps.open)
return (
<Button primary onClick={createToggle()}>
Copy link

Choose a reason for hiding this comment

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

The Button component could benefit from an aria-label for better accessibility, especially since it triggers a dropdown menu.

shouldIgnoreWhenClickMenu
>
<ul>
{props.data.isTheLatestSubmission && (
Copy link

Choose a reason for hiding this comment

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

Consider extracting the list items into separate components or functions for better readability and maintainability, especially since they have similar structures.

<li
className={classNames({
disabled:
props.isRemovingReviewSummations[props.data.id]
Copy link

Choose a reason for hiding this comment

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

The condition !(props.data.reviewSummation ?? []).length could be simplified to !props.data.reviewSummation?.length for better readability.

Copy link
Collaborator

Choose a reason for hiding this comment

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

@suppermancool - Can you fix please?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@jmgasper done in the latest commit

import { toast } from 'react-toastify'
import _ from 'lodash'

import { CREATE_BUS_EVENT_DATA_SUBMISSION_MARATHON_MATCH } from '../../config/busEvent.config'
Copy link

Choose a reason for hiding this comment

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

Consider renaming CREATE_BUS_EVENT_DATA_SUBMISSION_MARATHON_MATCH to a more concise name if possible, as it is quite long and may affect readability.

*/
export function useManageBusEvent(): useManageBusEventProps {
const [isRunningTest, setIsRunningTest] = useState<IsRemovingType>({})
const isRunningTestBool = useMemo(
Copy link

Choose a reason for hiding this comment

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

The use of lodash's some function is fine, but if performance is a concern, consider using native JavaScript methods like Object.values(isRunningTest).some(...).

}))
toast.success(`Run ${testType} test successfully`, {
toastId: 'Run test',
})
Copy link

Choose a reason for hiding this comment

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

The toastId value 'Run test' is hardcoded. If there are multiple types of tests, consider making this dynamic to avoid potential conflicts with other toasts.

handleError(e)
})
},
[],
Copy link

Choose a reason for hiding this comment

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

The dependency array for useCallback is empty. Ensure that this is intentional and that doPostBusEvent does not rely on any external variables that might change.

isLoadingRef.current = false
setIsLoading(isLoadingRef.current)
handleError(e)
fail()
Copy link

Choose a reason for hiding this comment

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

The function fail() is called here, but it is not defined or imported in this file. Ensure that fail() is defined or imported correctly, or remove this call if it is not needed.

})
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[],
Copy link

Choose a reason for hiding this comment

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

The dependency array for useCallback is empty, but the function doRemoveSubmission uses setIsRemovingSubmission, setMemberSubmissions, and handleError. Consider adding these dependencies to the array to ensure the callback updates correctly when these values change.

})
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[],
Copy link

Choose a reason for hiding this comment

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

The dependency array for useCallback is empty, but the function doRemoveReviewSummations uses setIsRemovingReviewSummations, setMemberSubmissions, and handleError. Consider adding these dependencies to the array to ensure the callback updates correctly when these values change.

@@ -0,0 +1,15 @@
/**
* Request to bust api
Copy link

Choose a reason for hiding this comment

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

The comment on line 2 seems to have a typo. It should likely be 'Request to bus API' instead of 'Request to bust api'.

Copy link
Collaborator

Choose a reason for hiding this comment

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

@suppermancool - Can you fix please?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@jmgasper done in the latest commit

export interface RequestBusAPI {
topic: string
originator: string
timestamp: string
Copy link

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 for the 'timestamp' field, such as 'Date' or a more precise string format, to ensure consistency and prevent errors.

topic: string
originator: string
timestamp: string
'mime-type': string
Copy link

Choose a reason for hiding this comment

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

The 'mime-type' field name uses a hyphen, which is unconventional in TypeScript interfaces. Consider using camelCase ('mimeType') for consistency with TypeScript naming conventions.

results = [...results, ...finalSubmission.submissions]
})

return finalSubmissions
Copy link

Choose a reason for hiding this comment

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

The function recalculateSubmissionRank returns finalSubmissions, which is of type MemberSubmission[], but the function signature suggests it should return Submission[]. Consider updating the return type in the function signature to match the actual return type.

_.each(data, (value, key) => {
result.push({
finalRank: undefined,
memberId: key as any,
Copy link

Choose a reason for hiding this comment

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

The memberId is being cast to any. Consider using a more specific type instead of any to ensure type safety.

* @returns updated data
*/
export function adjustSubmissionResponse(data: Submission): Submission {
const validReviews = _.reject(data.review, [
Copy link

Choose a reason for hiding this comment

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

The _.reject function is used to filter out reviews with a specific typeId. Ensure that EnvironmentConfig.ADMIN.AV_SCAN_SCORER_REVIEW_TYPE_ID is correctly defined and available in the environment configuration to avoid runtime errors.

data: SubmissionReview,
): SubmissionReview {
const created = data.created ? new Date(data.created) : data.created
const reviewedDate = data.created
Copy link

Choose a reason for hiding this comment

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

The condition data.created is checked for created, reviewedDate, and updated. If data.created is falsy, the original value is returned, which may not be the intended behavior for reviewedDate and updated. Consider checking the respective fields instead.

*/
export const reqToBusAPI = async (data: RequestBusAPI): Promise<string> => {
const resultData = await xhrPostAsync<RequestBusAPI, string>(
`${EnvironmentConfig.API.V5}/bus/events`,
Copy link

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 or exceptions that might occur during the API request.

): Promise<ApiV5ResponseSuccess> => {
for (const reviewSummationId of reviewSummationIds) {
// eslint-disable-next-line no-await-in-loop
await removeReviewSummation(reviewSummationId)
Copy link

Choose a reason for hiding this comment

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

Using await inside a loop can lead to performance issues as it waits for each promise to resolve before continuing to the next iteration. Consider using Promise.all to execute all promises concurrently if the order of execution is not important.

export const fetchSubmissionsOfChallenge = async (
challengeId: string,
): Promise<MemberSubmission[]> => {
if (!challengeId) {
Copy link

Choose a reason for hiding this comment

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

Consider throwing an error or logging a warning if challengeId is not provided, as returning an empty array might hide potential issues with the function usage.

export const removeSubmission = async (
submissionId: string,
): Promise<ApiV5ResponseSuccess> => {
const result = await xhrDeleteAsync<ApiV5ResponseSuccess>(
Copy link

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 API call to xhrDeleteAsync to manage potential failures or network issues gracefully.

maxFinalScore: number
submissions: MemberSubmission[]
} {
let maxFinalScore
Copy link

Choose a reason for hiding this comment

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

The initialization of maxFinalScore is a bit convoluted. Consider simplifying it by directly assigning a default value if the submissions array is empty or if the finalScore is undefined.

if (pA === pB) {
const timeA = _.get(a, 'submissions[0].submittedDate')
const timeB = _.get(b, 'submissions[0].submittedDate')
return timeA.getTime() - timeB.getTime()
Copy link

Choose a reason for hiding this comment

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

Ensure that timeA and timeB are not undefined before calling getTime() to avoid potential runtime errors.

if (pA === pB) {
const timeA = _.get(a, 'submissions[0].submittedDate')
const timeB = _.get(b, 'submissions[0].submittedDate')
return timeA.getTime() - timeB.getTime()
Copy link

Choose a reason for hiding this comment

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

Ensure that timeA and timeB are not undefined before calling getTime() to avoid potential runtime errors.

let pB = _.get(b, 'submissions[0]', { finalScore: 0 }).finalScore
if (pA === undefined) pA = 0
if (pB === undefined) pB = 0
if (pA > 0) maxFinalScore = pA
Copy link

Choose a reason for hiding this comment

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

The logic for updating maxFinalScore is repeated. Consider extracting this logic into a separate function to avoid duplication and improve readability.

*/
function removeDecimal(num: number): string | undefined {
// eslint-disable-next-line prefer-regex-literals
const re = new RegExp('^-?\\d+')
Copy link

Choose a reason for hiding this comment

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

Consider using a literal regex /^-?\d+/ instead of new RegExp('^-?\\d+') for better readability and performance.

* @returns number
*/
function toAcurateFixed(num: number, decimal: number): string | undefined {
const re = new RegExp(`^-?\\d+(?:.\\d{0,${decimal}})?`)
Copy link

Choose a reason for hiding this comment

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

Consider using a literal regex /^-?\d+(?:\.\d{0,${decimal}})?/ instead of new RegExp(^-?\d+(?:.\d{0,${decimal}})?) for better readability and performance. Note the escaped dot \. to match a literal period.

* @param decimal number of unit to fix
* @returns number
*/
function toAcurateFixed(num: number, decimal: number): string | undefined {
Copy link

Choose a reason for hiding this comment

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

The function name toAcurateFixed seems to have a typo. Consider renaming it to toAccurateFixed for clarity.

return num
}

if (_.isNaN(parseFloat(num as string))) return num as number
Copy link

Choose a reason for hiding this comment

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

The use of _.isNaN(parseFloat(num as string)) can be replaced with isNaN(Number(num)) for simplicity and to avoid unnecessary lodash dependency.

Copy link
Collaborator

Choose a reason for hiding this comment

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

@suppermancool - Can you fix please?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@jmgasper done in the latest commit

if (_.isNaN(parseFloat(num as string))) return num as number
const numFloat = parseFloat(num as string)

const result = _.toFinite(toAcurateFixed(numFloat, decimal))
Copy link

Choose a reason for hiding this comment

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

The use of _.toFinite may not be necessary here. Consider directly using parseFloat or Number to convert the string to a number, as _.toFinite will convert NaN to 0, which might not be the intended behavior.

const numFloat = parseFloat(num as string)

const result = _.toFinite(toAcurateFixed(numFloat, decimal))
const integerResult = _.toFinite(removeDecimal(numFloat))
Copy link

Choose a reason for hiding this comment

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

The use of _.toFinite may not be necessary here. Consider directly using parseFloat or Number to convert the string to a number, as _.toFinite will convert NaN to 0, which might not be the intended behavior.

* @returns true if the object is a string, false if otherwise
*/
export function isStringObject(str: any): boolean {
if (typeof str === 'string' || str instanceof String) {
Copy link

Choose a reason for hiding this comment

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

Consider using strict equality checks (===) for type comparison to avoid potential issues with type coercion.

*/
export function isStringObject(str: any): boolean {
if (typeof str === 'string' || str instanceof String) {
return true
Copy link

Choose a reason for hiding this comment

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

Add a semicolon at the end of the statement to maintain consistency with the rest of the codebase.

Copy link
Collaborator

Choose a reason for hiding this comment

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

@suppermancool - Can you fix please?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@jmgasper the ESLint requires removing the semicolon at the end of the statement

return true
}

return false
Copy link

Choose a reason for hiding this comment

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

Add a semicolon at the end of the statement to maintain consistency with the rest of the codebase.

Copy link
Collaborator

Choose a reason for hiding this comment

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

@suppermancool - Can you fix please?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@jmgasper the ESLint requires removing the semicolon at the end of the statement

) : (
<div className={styles.blockTableContainer}>
<SubmissionTable
data={submissions}
Copy link

Choose a reason for hiding this comment

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

The prop name datas has been changed to data. Ensure that this change is consistent throughout the codebase where this component is used to prevent any potential issues.


interface Props {
className?: string
data: Submission[]
Copy link

Choose a reason for hiding this comment

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

The variable name datas has been corrected to data, which is more appropriate and follows standard naming conventions. Ensure that all references to this variable in the code are updated accordingly to prevent any runtime errors.

return (
<TableWrapper className={classNames(styles.container, props.className)}>
{isTablet ? (
<TableMobile columns={columnsMobile} data={props.data} />
Copy link

Choose a reason for hiding this comment

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

The prop data is being used instead of datas. Ensure that props.data is correctly defined and used throughout the component to avoid potential runtime errors.

) : (
<Table
columns={columns}
data={props.data}
Copy link

Choose a reason for hiding this comment

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

The prop data is being used instead of datas. Ensure that props.data is correctly defined and used throughout the component to avoid potential runtime errors.

@jmgasper jmgasper merged commit b261088 into feat/system-admin Jun 4, 2025
3 checks passed
@suppermancool suppermancool deleted the diazz-admin-code-30376878 branch June 4, 2025 04:54
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.

2 participants