Skip to content

PM-4477 Show failed submission in appeals response#1557

Closed
himaniraghav3 wants to merge 2 commits intodevfrom
PM-4477
Closed

PM-4477 Show failed submission in appeals response#1557
himaniraghav3 wants to merge 2 commits intodevfrom
PM-4477

Conversation

@himaniraghav3
Copy link
Copy Markdown
Collaborator

@himaniraghav3 himaniraghav3 commented Mar 30, 2026

Related JIRA Ticket:

https://topcoder.atlassian.net/browse/PM-4477

What's in this PR?

Show failed submissions in the Appeals Response phase


Open with Devin

devin-ai-integration[bot]

This comment was marked as resolved.

@himaniraghav3 himaniraghav3 requested a review from kkartunov March 31, 2026 04:13
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

View 5 additional findings in Devin Review.

Open in Devin Review

Comment on lines +277 to +371
if (canViewAsSubmitter) {
// For reviewer-only rows, use existing filter
const reviewerOnlyRows = aggregatedRows.filter(row => matchesReviewer(row) && !matchesSubmitter(row))

// For submitter rows, include both aggregated and non-aggregated submissions
// Apply restrictToLatest filtering to respect submission limits
const submissionsForSubmitterRows = restrictToLatest && hasLatestFlag
? (filteredChallengeSubmissions ?? []).filter(submission => submission.isLatest)
: (filteredChallengeSubmissions ?? [])

const submitterRows = submissionsForSubmitterRows
.filter(submission => {
const memberId = submission.memberId
if (!memberId || !ownedMemberIds.has(memberId)) {
return false
}

const submissionType = submission.type?.toUpperCase()
return !submissionType || submissionType === 'CONTEST_SUBMISSION'
})
.map(submission => {
// Use aggregated data if available, otherwise create minimal aggregated structure
const aggregatedRow = aggregatedRows.find(row => row.id === submission.id)
if (aggregatedRow) {
return aggregatedRow
}

// Create minimal aggregated structure for non-reviewed submissions
// This ensures buildSubmissionReviewerRows creates at least one row
const submitterHandle = submission.userInfo?.memberHandle
?? submission.review?.submitterHandle
?? undefined
const submitterColor = submission.userInfo?.handleColor
?? submission.review?.submitterHandleColor
?? undefined

// Build review details array from all reviewInfos or single review
const allReviewInfos = submission.reviewInfos && submission.reviewInfos.length > 0
? submission.reviewInfos
: submission.review
? [submission.review]
: []

const reviewDetails = allReviewInfos
.map((reviewInfo: any) => {
const reviewId = reviewInfo.id
const appealInfo = reviewId ? mappingReviewAppeal[reviewId] : undefined
const totalAppeals = appealInfo?.totalAppeals ?? 0
const finishedAppeals = appealInfo?.finishAppeals ?? 0

// Resolve review date from multiple sources
const reviewDateString = reviewInfo.reviewDateString
?? reviewInfo.updatedAtString
?? (reviewInfo as any).createdAtString
?? (reviewInfo as any).reviewedAt

return {
// Review detail with reviewer information from reviewInfo
finalScore: reviewInfo.finalScore,
finishedAppeals,
resourceId: reviewInfo.resourceId,
reviewDateString,
reviewerHandle: reviewInfo.reviewerHandle ?? undefined,
reviewerHandleColor: reviewInfo.reviewerHandleColor,
reviewInfo: {
...reviewInfo,
reviewDateString,
},
totalAppeals,
unresolvedAppeals: totalAppeals - finishedAppeals,
}
})
.reverse()

// Get latest review date from reviewDetails
const latestReviewDate = reviewDetails.length > 0
? reviewDetails[0]?.reviewDateString
: undefined

return {
...submission,
aggregated: {
id: submission.id,
latestReviewDateString: latestReviewDate,
reviews: reviewDetails,
submission,
submitterHandle,
submitterHandleColor: submitterColor,
},
}
})

// Combine submitter rows and reviewer-only rows
return [...reviewerOnlyRows, ...submitterRows]
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔴 Submitter rows dropped when filteredChallengeSubmissions is empty

The new canViewAsSubmitter code block (line 277) sources submitter rows exclusively from filteredChallengeSubmissions, which derives from challengeInfo?.submissions (TableAppealsResponse.tsx:166-177). When challengeInfo is undefined (the default context value per ChallengeDetailContext.ts:12) or has no submissions, filteredChallengeSubmissions is [], so submissionsForSubmitterRows is also [] and submitterRows will be empty. Meanwhile, reviewerOnlyRows at line 279 explicitly excludes any row where matchesSubmitter(row) is true. The combined result [...reviewerOnlyRows, ...submitterRows] therefore drops all submitter-owned rows that exist in aggregatedRows.

The old code at line 373 (aggregatedRows.filter(row => matchesSubmitter(row) || matchesReviewer(row))) would correctly return submitter-matching rows from aggregatedRows regardless of filteredChallengeSubmissions being empty. This regression means submitters may see no rows during loading or when challenge submission data is incomplete.

Prompt for agents
In src/apps/review/src/lib/components/TableAppealsResponse/TableAppealsResponse.tsx, the canViewAsSubmitter block (lines 277-371) sources submitter rows from filteredChallengeSubmissions. When that array is empty (e.g. challengeInfo?.submissions not yet loaded), submitter rows are dropped because they are excluded from reviewerOnlyRows via !matchesSubmitter(row) but never appear in submitterRows.

Fix: Add a fallback so that when filteredChallengeSubmissions is empty (or has no valid IDs), submitter rows should still come from aggregatedRows. For example, at line 283, before computing submissionsForSubmitterRows, check if filteredChallengeSubmissions is empty. If it is, fall back to the old behavior:

  if (!filteredChallengeSubmissions || filteredChallengeSubmissions.length === 0) {
      return aggregatedRows.filter(row => matchesSubmitter(row) || matchesReviewer(row))
  }

Alternatively, ensure that submitterRows is computed from aggregatedRows as a base and only augmented with additional entries from filteredChallengeSubmissions for non-reviewed submissions.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@himaniraghav3 please check and fix.

Comment on lines +277 to +371
if (canViewAsSubmitter) {
// For reviewer-only rows, use existing filter
const reviewerOnlyRows = aggregatedRows.filter(row => matchesReviewer(row) && !matchesSubmitter(row))

// For submitter rows, include both aggregated and non-aggregated submissions
// Apply restrictToLatest filtering to respect submission limits
const submissionsForSubmitterRows = restrictToLatest && hasLatestFlag
? (filteredChallengeSubmissions ?? []).filter(submission => submission.isLatest)
: (filteredChallengeSubmissions ?? [])

const submitterRows = submissionsForSubmitterRows
.filter(submission => {
const memberId = submission.memberId
if (!memberId || !ownedMemberIds.has(memberId)) {
return false
}

const submissionType = submission.type?.toUpperCase()
return !submissionType || submissionType === 'CONTEST_SUBMISSION'
})
.map(submission => {
// Use aggregated data if available, otherwise create minimal aggregated structure
const aggregatedRow = aggregatedRows.find(row => row.id === submission.id)
if (aggregatedRow) {
return aggregatedRow
}

// Create minimal aggregated structure for non-reviewed submissions
// This ensures buildSubmissionReviewerRows creates at least one row
const submitterHandle = submission.userInfo?.memberHandle
?? submission.review?.submitterHandle
?? undefined
const submitterColor = submission.userInfo?.handleColor
?? submission.review?.submitterHandleColor
?? undefined

// Build review details array from all reviewInfos or single review
const allReviewInfos = submission.reviewInfos && submission.reviewInfos.length > 0
? submission.reviewInfos
: submission.review
? [submission.review]
: []

const reviewDetails = allReviewInfos
.map((reviewInfo: any) => {
const reviewId = reviewInfo.id
const appealInfo = reviewId ? mappingReviewAppeal[reviewId] : undefined
const totalAppeals = appealInfo?.totalAppeals ?? 0
const finishedAppeals = appealInfo?.finishAppeals ?? 0

// Resolve review date from multiple sources
const reviewDateString = reviewInfo.reviewDateString
?? reviewInfo.updatedAtString
?? (reviewInfo as any).createdAtString
?? (reviewInfo as any).reviewedAt

return {
// Review detail with reviewer information from reviewInfo
finalScore: reviewInfo.finalScore,
finishedAppeals,
resourceId: reviewInfo.resourceId,
reviewDateString,
reviewerHandle: reviewInfo.reviewerHandle ?? undefined,
reviewerHandleColor: reviewInfo.reviewerHandleColor,
reviewInfo: {
...reviewInfo,
reviewDateString,
},
totalAppeals,
unresolvedAppeals: totalAppeals - finishedAppeals,
}
})
.reverse()

// Get latest review date from reviewDetails
const latestReviewDate = reviewDetails.length > 0
? reviewDetails[0]?.reviewDateString
: undefined

return {
...submission,
aggregated: {
id: submission.id,
latestReviewDateString: latestReviewDate,
reviews: reviewDetails,
submission,
submitterHandle,
submitterHandleColor: submitterColor,
},
}
})

// Combine submitter rows and reviewer-only rows
return [...reviewerOnlyRows, ...submitterRows]
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@himaniraghav3 please check and fix.

@himaniraghav3
Copy link
Copy Markdown
Collaborator Author

I'll create a new one. This one got too crowded and out of control

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