Skip to content

Pr 486#490

Merged
Benjtalkshow merged 11 commits intoboundlessfi:mainfrom
Benjtalkshow:pr-486
Mar 16, 2026
Merged

Pr 486#490
Benjtalkshow merged 11 commits intoboundlessfi:mainfrom
Benjtalkshow:pr-486

Conversation

@Benjtalkshow
Copy link
Collaborator

@Benjtalkshow Benjtalkshow commented Mar 16, 2026

Summary by CodeRabbit

  • New Features

    • Added search capability for participants with real-time filtering
    • Added participant type filter (All, Individual, Team)
    • Added search functionality for submissions
    • Integrated pagination controls for submission tables
  • Bug Fixes

    • Improved handling of missing profile images and avatars
    • Fixed submission status tracking to include additional states

@vercel
Copy link

vercel bot commented Mar 16, 2026

@Benjtalkshow is attempting to deploy a commit to the Threadflow Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link

coderabbitai bot commented Mar 16, 2026

📝 Walkthrough

Walkthrough

This PR adds search and type filtering to participant lists with client-side debouncing, introduces search filtering for submissions, refactors submission pagination using TanStack React Table, shifts participant filtering responsibility to the backend, and widens avatar fields across types to allow null values for improved image handling.

Changes

Cohort / File(s) Summary
Participant filtering and search
app/(landing)/hackathons/[slug]/components/tabs/contents/Participants.tsx, components/organization/hackathons/ParticipantsTable.tsx
Added client-side search with debouncing and type filter ('individual' | 'team'); updated avatar handling to fallback to undefined for missing images.
Submission pagination and table integration
app/(landing)/organizations/[id]/hackathons/[hackathonId]/submissions/page.tsx
Integrated TanStack React Table for manual pagination control; replaced submissions with allSubmissions; removed initial fetch call from hackathon effect; added DataTablePagination component.
Filter logic and query parameters
app/(landing)/organizations/[id]/hackathons/[hackathonId]/participants/page.tsx, hooks/hackathon/use-hackathon-queries.ts, lib/api/hackathon.ts
Extended query param interfaces to include search and type fields; updated API methods to append new parameters; shifted filtering responsibility to backend with comment noting removal of frontend filtering.
Hook updates for organizer submissions
hooks/hackathon/use-organizer-submissions.ts
Added debounced search filter; removed 'WITHDRAWN' from status values; introduced updateLimit method for pagination; refactored fetch logic into separate effect triggered by filter/pagination changes.
Registration hook status expansion
hooks/hackathon/use-register-hackathon.ts
Extended hasSubmitted to return true for 'SUBMITTED', 'SHORTLISTED', or 'DISQUALIFIED' statuses instead of only 'SUBMITTED'.
Hackathons hook filter refactoring
hooks/use-hackathons.ts
Broadened participantFilters.status and type from strict unions to generic strings; recomputed pagination metadata (page, limit, total) to derive currentPage and totalPages; updated hasNext/hasPrev logic.
Avatar nullability across types
lib/api/hackathons.ts, types/hackathon/participant.ts, hooks/use-participant-submission.ts, components/hackathons/submissions/submissionCard...tsx, components/organization/cards/*, components/organization/hackathons/ParticipantsTable.tsx
Widened avatar and image fields across API types, component props, and hooks to allow string | null; updated rendering logic to pass undefined when avatar is falsy, ensuring clean AvatarImage component handling.
Participant matching expansion
app/(landing)/hackathons/[slug]/components/header/ActionButtons.tsx
Extended isParticipant check to also match nested user.id in addition to userId, broadening participant detection criteria.

Sequence Diagram(s)

sequenceDiagram
    participant User as User
    participant ParticipantsUI as Participants Component
    participant DebounceHook as Debounce Hook
    participant QueryHook as use-hackathon-queries
    participant API as Backend API
    participant State as Local State

    User->>ParticipantsUI: Enter search term
    ParticipantsUI->>DebounceHook: Trigger debounce (500ms)
    Note over DebounceHook: Waiting for user to stop typing
    User->>ParticipantsUI: Stop typing
    DebounceHook->>State: Update debouncedSearch
    State->>QueryHook: Pass search & type params
    QueryHook->>API: Fetch with search + type filters
    API->>QueryHook: Return filtered participants
    QueryHook->>ParticipantsUI: Update participants list
    ParticipantsUI->>User: Display filtered results
Loading
sequenceDiagram
    participant Page as Submissions Page
    participant TanStackTable as TanStack React Table
    participant State as Pagination State
    participant QueryHook as use-organizer-submissions
    participant API as Backend API

    Page->>TanStackTable: Initialize with allSubmissions data
    Page->>State: Set initial pagination (page, limit)
    User->>TanStackTable: Click next page button
    TanStackTable->>State: Trigger updateLimit or goToPage
    State->>QueryHook: Update pagination state
    QueryHook->>API: Fetch submissions with new page/limit
    API->>QueryHook: Return paginated results
    QueryHook->>State: Update allSubmissions & pagination metadata
    State->>TanStackTable: Trigger re-render with new data
    TanStackTable->>Page: Display next page of submissions
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • Pr 486 #488: Modifies participant and submission pages with overlapping changes to filtering, pagination, status/type handling, and API query parameter support.
  • Feat/update,refactor,improve #436: Touches overlapping hackathon-related hooks (use-organizer-submissions) and API types (lib/api/hackathons) with related filtering and status logic updates.
  • UI fixes #454: Changes useRegisterHackathon hook's hasSubmitted return value logic, directly affecting the same hook being modified in this PR.

Suggested reviewers

  • 0xdevcollins

🐰 A rabbit hops through filters bright,
Searching, typing, pure delight!
Tables paginate with grace,
Null avatars find their place,
All the data flows just right!

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Pr 486' is a generic reference number with no meaningful description of the actual changes made to the codebase. Replace 'Pr 486' with a descriptive title that summarizes the main changes, such as 'Add search and type filters to hackathon participants and submissions' or similar.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (4)
app/(landing)/hackathons/[slug]/components/tabs/contents/Participants.tsx (1)

157-163: Use named handle* callbacks for the new control events.

The added onChange/onClick handlers are inline. Please extract them to named handlers (handleSearchChange, handleParticipationTypeChange) to match project conventions.

As per coding guidelines: "Event handlers should start with 'handle' prefix (e.g., handleClick, handleSubmit)".

Also applies to: 219-229

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/`(landing)/hackathons/[slug]/components/tabs/contents/Participants.tsx
around lines 157 - 163, Extract inline event handlers into named functions:
replace the inline onChange on the search input that uses setSearchQuery with a
new handleSearchChange(e: React.ChangeEvent<HTMLInputElement>) that calls
setSearchQuery(e.target.value) and use handleSearchChange in the JSX; likewise
replace the inline onClick/onChange used for the participation type control
(lines around the participationType selection) with
handleParticipationTypeChange(newType: string | ChangeEvent) that updates the
corresponding state. Add these new functions near the top of the Participants
component (next to existing state hooks like searchQuery and setSearchQuery) and
update the JSX to reference handleSearchChange and handleParticipationTypeChange
to follow the project's "handle*" event naming convention.
app/(landing)/organizations/[id]/hackathons/[hackathonId]/submissions/page.tsx (1)

4-4: Remove unused useMemo import.

useMemo is imported but not used in this file.

♻️ Remove unused import
-import { useEffect, useState, useMemo } from 'react';
+import { useEffect, useState } from 'react';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/`(landing)/organizations/[id]/hackathons/[hackathonId]/submissions/page.tsx
at line 4, Remove the unused React import useMemo from the top of the
submissions page component; edit the import statement that currently reads
"import { useEffect, useState, useMemo } from 'react';" and remove useMemo so it
becomes "import { useEffect, useState } from 'react';" (this affects the module
that defines the submissions page component in this file).
hooks/use-hackathons.ts (1)

33-37: Consider preserving stricter filter types.

Broadening status and type from specific union types to generic string reduces compile-time type safety. If the allowed filter values are known and stable, keeping the union types helps catch typos and invalid values at compile time.

♻️ Optional: Preserve type safety with unions
 participantFilters?: {
-  status?: string;
-  type?: string;
+  status?: 'submitted' | 'not_submitted' | 'shortlisted' | 'disqualified';
+  type?: 'individual' | 'team';
   search?: string;
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hooks/use-hackathons.ts` around lines 33 - 37, The participantFilters type
has widened status and type to generic string which loses compile-time safety;
revert these fields to their specific union types (or existing enums) instead of
string by updating the participantFilters declaration in use-hackathons.ts so
that status and type use the known allowed literal unions (e.g., "active" |
"pending" | "archived") or reuse existing types/enums from your domain (e.g.,
ParticipantStatus, ParticipantType), and update any call sites to conform to
those unions.
hooks/hackathon/use-organizer-submissions.ts (1)

108-117: fetchSubmissions in dependency array creates indirect coupling.

Including fetchSubmissions in the dependency array means the effect runs whenever fetchSubmissions is recreated (due to its own dependencies changing). Since debouncedSearch, filters.status, filters.type, and pagination.limit are also direct dependencies of this effect AND in fetchSubmissions's dependencies, there's redundancy that makes the data flow harder to trace.

Consider removing fetchSubmissions from the dependency array and calling the fetch logic directly, or using a ref to avoid the coupling.

♻️ Simpler approach using a fetch ref
+  const fetchRef = useRef(fetchSubmissions);
+  fetchRef.current = fetchSubmissions;
+
   // Sync with backend on filter/pagination changes
   useEffect(() => {
-    fetchSubmissions(1);
-  }, [
-    fetchSubmissions,
-    debouncedSearch,
-    filters.status,
-    filters.type,
-    pagination.limit,
-  ]);
+    fetchRef.current(1);
+  }, [debouncedSearch, filters.status, filters.type, pagination.limit]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hooks/hackathon/use-organizer-submissions.ts` around lines 108 - 117, The
effect is indirectly coupled to fetchSubmissions because it's included in the
dependency array; remove fetchSubmissions from the useEffect deps and instead
call the fetch logic via a stable ref or inline the call to avoid redundant
reruns. Concretely, create a ref (e.g., fetchSubmissionsRef) and set
fetchSubmissionsRef.current = fetchSubmissions wherever fetchSubmissions is
defined/updated, then in the useEffect (which should depend only on
debouncedSearch, filters.status, filters.type, pagination.limit) call
fetchSubmissionsRef.current(1); alternatively, move the minimal fetch logic used
by fetchSubmissions directly into the useEffect so the deps are only
debouncedSearch, filters.status, filters.type, pagination.limit.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@components/organization/hackathons/ParticipantsTable.tsx`:
- Around line 113-116: The status narrowing in ParticipantsTable incorrectly
casts submission.status to only 'SHORTLISTED'|'DISQUALIFIED'|'SUBMITTED',
causing other upstream values (e.g., WITHDRAWN) to be misrendered and action
logic to misbehave; update the status handling to be exhaustive by deriving
status from the real upstream enum/type (or using a safe switch/map over
submission.status) and add an explicit case/default for other statuses (e.g.,
'WITHDRAWN' -> 'Withdrawn', unknown -> 'Unknown' or a disabled state). Apply
this change wherever status is used (the initial cast of submission.status, the
rendering branch around 121-133, and the action logic that reads status for
deciding actions like at the current action handler) so display labels and
allowed actions remain consistent for all possible upstream values.

In `@components/organization/hackathons/submissions/SubmissionsManagement.tsx`:
- Line 57: The onPageSizeChange prop declared in the component props interface
is never used; update the SubmissionsManagement component by either removing
onPageSizeChange from the props interface to eliminate dead API, or add it to
the function parameter destructuring for SubmissionsManagement and wire it to
the page-size change logic (e.g., call onPageSizeChange(size) from the existing
page size change handler or pass it into the Pagination/PageSize selector you
use) so the external callback is invoked when page size changes.

In `@hooks/hackathon/use-register-hackathon.ts`:
- Line 125: The hasSubmitted boolean currently only checks for
participant?.submission?.status === 'SUBMITTED' which will be false for
participants whose submissions are in SHORTLISTED or DISQUALIFIED states; update
the logic in use-register-hackathon.ts (the hasSubmitted property) to treat
those statuses as submitted — e.g. check if participant?.submission?.status is
in the accepted set ['SUBMITTED','SHORTLISTED','DISQUALIFIED'] or,
alternatively, check that a submission exists and its status is not a
draft/incomplete state, then return true.

In `@lib/api/hackathon.ts`:
- Around line 109-115: The getHackathonSubmissions call is appending unsupported
filters (search) to the request causing silent unfiltered results; remove any
code that appends params.search (and other unsupported filters like sort) when
building the query so only page and limit are sent, update the params
type/signature to only expose page?: number and limit?: number, and ensure
callers (e.g., submissions page/hooks) perform client-side filtering instead of
relying on server-side search.

In `@lib/api/hackathons.ts`:
- Around line 697-702: The participant.image property is declared as optional
string (image?: string) but the backend can return null; update the participant
type so image allows null (e.g., image?: string | null) wherever the participant
shape is defined (the participant property in the hackathons API types) so the
generated types match backend responses.

---

Nitpick comments:
In `@app/`(landing)/hackathons/[slug]/components/tabs/contents/Participants.tsx:
- Around line 157-163: Extract inline event handlers into named functions:
replace the inline onChange on the search input that uses setSearchQuery with a
new handleSearchChange(e: React.ChangeEvent<HTMLInputElement>) that calls
setSearchQuery(e.target.value) and use handleSearchChange in the JSX; likewise
replace the inline onClick/onChange used for the participation type control
(lines around the participationType selection) with
handleParticipationTypeChange(newType: string | ChangeEvent) that updates the
corresponding state. Add these new functions near the top of the Participants
component (next to existing state hooks like searchQuery and setSearchQuery) and
update the JSX to reference handleSearchChange and handleParticipationTypeChange
to follow the project's "handle*" event naming convention.

In
`@app/`(landing)/organizations/[id]/hackathons/[hackathonId]/submissions/page.tsx:
- Line 4: Remove the unused React import useMemo from the top of the submissions
page component; edit the import statement that currently reads "import {
useEffect, useState, useMemo } from 'react';" and remove useMemo so it becomes
"import { useEffect, useState } from 'react';" (this affects the module that
defines the submissions page component in this file).

In `@hooks/hackathon/use-organizer-submissions.ts`:
- Around line 108-117: The effect is indirectly coupled to fetchSubmissions
because it's included in the dependency array; remove fetchSubmissions from the
useEffect deps and instead call the fetch logic via a stable ref or inline the
call to avoid redundant reruns. Concretely, create a ref (e.g.,
fetchSubmissionsRef) and set fetchSubmissionsRef.current = fetchSubmissions
wherever fetchSubmissions is defined/updated, then in the useEffect (which
should depend only on debouncedSearch, filters.status, filters.type,
pagination.limit) call fetchSubmissionsRef.current(1); alternatively, move the
minimal fetch logic used by fetchSubmissions directly into the useEffect so the
deps are only debouncedSearch, filters.status, filters.type, pagination.limit.

In `@hooks/use-hackathons.ts`:
- Around line 33-37: The participantFilters type has widened status and type to
generic string which loses compile-time safety; revert these fields to their
specific union types (or existing enums) instead of string by updating the
participantFilters declaration in use-hackathons.ts so that status and type use
the known allowed literal unions (e.g., "active" | "pending" | "archived") or
reuse existing types/enums from your domain (e.g., ParticipantStatus,
ParticipantType), and update any call sites to conform to those unions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d13e7d13-2915-41fc-b2b4-442226aaf4e5

📥 Commits

Reviewing files that changed from the base of the PR and between ed65b36 and 46e4461.

📒 Files selected for processing (15)
  • app/(landing)/hackathons/[slug]/components/tabs/contents/Participants.tsx
  • app/(landing)/organizations/[id]/hackathons/[hackathonId]/participants/page.tsx
  • app/(landing)/organizations/[id]/hackathons/[hackathonId]/submissions/page.tsx
  • components/hackathons/submissions/SubmissionDetailModal.tsx
  • components/organization/cards/Participant.tsx
  • components/organization/hackathons/ParticipantsGrid.tsx
  • components/organization/hackathons/ParticipantsTable.tsx
  • components/organization/hackathons/submissions/SubmissionsManagement.tsx
  • hooks/hackathon/use-hackathon-queries.ts
  • hooks/hackathon/use-organizer-submissions.ts
  • hooks/hackathon/use-register-hackathon.ts
  • hooks/use-hackathons.ts
  • hooks/use-submission-actions.ts
  • lib/api/hackathon.ts
  • lib/api/hackathons.ts

loading: boolean;
onFilterChange: (filters: SubmissionFilters) => void;
onPageChange: (page: number) => void;
onPageSizeChange?: (size: number) => void;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

onPageSizeChange prop is defined but never used.

The onPageSizeChange prop is added to the interface but is not destructured in the component function parameters (lines 69-81), making it dead API surface. Either remove it from the interface or add it to the destructured parameters and wire it up.

🔧 Proposed fix: Remove unused prop or wire it up

Option A: Remove the unused prop

 interface SubmissionsManagementProps {
   ...
   onPageChange: (page: number) => void;
-  onPageSizeChange?: (size: number) => void;
   onRefresh: () => void;

Option B: Destructure and use it

 export function SubmissionsManagement({
   ...
   onPageChange,
+  onPageSizeChange,
   onRefresh,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
onPageSizeChange?: (size: number) => void;
interface SubmissionsManagementProps {
organizationId: string;
hackathonId: string;
onPageChange: (page: number) => void;
onRefresh: () => void;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/organization/hackathons/submissions/SubmissionsManagement.tsx` at
line 57, The onPageSizeChange prop declared in the component props interface is
never used; update the SubmissionsManagement component by either removing
onPageSizeChange from the props interface to eliminate dead API, or add it to
the function parameter destructuring for SubmissionsManagement and wire it to
the page-size change logic (e.g., call onPageSizeChange(size) from the existing
page size change handler or pass it into the Pagination/PageSize selector you
use) so the external callback is invoked when page size changes.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (2)
lib/api/hackathons.ts (1)

642-680: Inconsistent avatar nullability in related types.

While ParticipantTeamMember.avatar and Participant.user.profile.image have been widened to string | null, the following nested avatar fields remain as avatar?: string:

  • Line 651: ParticipantVote.user.profile.avatar
  • Line 668: ParticipantComment.user.profile.avatar
  • Line 710: ParticipantSubmission.reviewedBy.avatar

If the backend can return null for these avatars as well, consider widening them for consistency.

🔧 Suggested consistency fix
 export interface ParticipantVote {
   id: string;
   userId: string;
   user: {
     id: string;
     profile: {
       firstName: string;
       lastName: string;
       username: string;
-      avatar?: string;
+      avatar?: string | null;
     };
     email: string;
   };
   // ...
 }

 export interface ParticipantComment {
   id: string;
   userId: string;
   user: {
     id: string;
     profile: {
       firstName: string;
       lastName: string;
       username: string;
-      avatar?: string;
+      avatar?: string | null;
     };
     email: string;
   };
   // ...
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/api/hackathons.ts` around lines 642 - 680, The nested avatar fields are
inconsistently optional strings while other related types use string | null;
update the avatar types to match by changing ParticipantVote.user.profile.avatar
and ParticipantComment.user.profile.avatar from avatar?: string to avatar:
string | null, and also update ParticipantSubmission.reviewedBy.avatar to
avatar: string | null (ensure the ParticipantVote, ParticipantComment, and
ParticipantSubmission interfaces and any nested reviewedBy type are adjusted so
consumers handle null consistently).
types/hackathon/participant.ts (1)

24-97: Consolidate duplicate type definitions to a single source.

This file defines ParticipantTeamMember, ParticipantVote, ParticipantComment, ParticipantSubmission, and Participant interfaces that are also defined in lib/api/hackathons.ts. Type inconsistencies exist across these duplicates:

  • ParticipantVote and ParticipantComment: avatar?: string | null in types/ vs avatar?: string in lib/api/
  • ParticipantSubmission: Similar avatar nullability difference, plus additional structural divergence (lib/api version includes submittedAt, participationType, participant fields)
  • Additional duplication: ParticipantsData and Participant interfaces are also defined in both locations

This duplication increases type drift risk. Since lib/api/hackathons.ts is imported by significantly more files across the codebase, consider either:

  1. Consolidating all types in types/hackathon/participant.ts and importing in lib/api/hackathons.ts
  2. Ensuring both files stay strictly synchronized
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@types/hackathon/participant.ts` around lines 24 - 97, This file duplicates
participant-related interfaces already defined in lib/api/hackathons.ts
(ParticipantTeamMember, ParticipantVote, ParticipantComment,
ParticipantSubmission, Participant and ParticipantsData) causing nullability and
shape drift (e.g. avatar?: string | null vs avatar?: string, and fields like
submittedAt/participationType/participant missing). Fix by consolidating to a
single source of truth: remove the duplicate interfaces from
types/hackathon/participant.ts and import the canonical types from
lib/api/hackathons.ts (or vice-versa if you choose the types file as canonical),
update all references to use the imported types, and reconcile nullability/extra
fields so ParticipantVote, ParticipantComment, ParticipantSubmission and
ParticipantTeamMember align exactly with the canonical definitions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/`(landing)/hackathons/[slug]/components/header/ActionButtons.tsx:
- Around line 41-42: Replace the unsafe cast in the participant identity check:
in the arrow predicate that currently uses "(p as any).userId === user.id ||
p.user?.id === user.id", remove the any cast and use the typed property
"p.userId" instead (i.e., "p.userId === user.id || p.user?.id === user.id") so
the Participant type is respected; ensure the predicate parameter remains typed
as Participant and that optional chaining on p.user?.id is preserved.

In `@components/hackathons/submissions/submissionCard.tsx`:
- Line 31: The header avatar currently constructs `url(${submitterAvatar})` even
when `submitterAvatar` can be null; update the component (submissionCard / the
header avatar rendering) to use a safe fallback image or initials when
`submitterAvatar` is null or empty—e.g., compute a resolvedAvatar =
submitterAvatar ?? '<fallback-url-or-empty>' (or conditionally render a
placeholder/avatar-initials) and use that value wherever
`url(${submitterAvatar})` is used so the UI never attempts to load a null URL.

In `@components/organization/cards/ReviewSubmissionModal/types.ts`:
- Line 5: SubmissionCommentsTab passes comment.author.avatar (now typed string |
null) directly to AvatarImage; make it null-safe by normalizing to undefined
like the other tabs—replace the direct prop with comment.author.avatar ||
undefined in SubmissionCommentsTab (the prop passed to AvatarImage around the
current line 41) so it matches the pattern used in TeamSection.tsx and
SubmissionVotesTab.tsx.

---

Nitpick comments:
In `@lib/api/hackathons.ts`:
- Around line 642-680: The nested avatar fields are inconsistently optional
strings while other related types use string | null; update the avatar types to
match by changing ParticipantVote.user.profile.avatar and
ParticipantComment.user.profile.avatar from avatar?: string to avatar: string |
null, and also update ParticipantSubmission.reviewedBy.avatar to avatar: string
| null (ensure the ParticipantVote, ParticipantComment, and
ParticipantSubmission interfaces and any nested reviewedBy type are adjusted so
consumers handle null consistently).

In `@types/hackathon/participant.ts`:
- Around line 24-97: This file duplicates participant-related interfaces already
defined in lib/api/hackathons.ts (ParticipantTeamMember, ParticipantVote,
ParticipantComment, ParticipantSubmission, Participant and ParticipantsData)
causing nullability and shape drift (e.g. avatar?: string | null vs avatar?:
string, and fields like submittedAt/participationType/participant missing). Fix
by consolidating to a single source of truth: remove the duplicate interfaces
from types/hackathon/participant.ts and import the canonical types from
lib/api/hackathons.ts (or vice-versa if you choose the types file as canonical),
update all references to use the imported types, and reconcile nullability/extra
fields so ParticipantVote, ParticipantComment, ParticipantSubmission and
ParticipantTeamMember align exactly with the canonical definitions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 01d9be19-fa16-4a02-8d14-b69cad9cfb64

📥 Commits

Reviewing files that changed from the base of the PR and between 46e4461 and 803c401.

📒 Files selected for processing (14)
  • app/(landing)/hackathons/[slug]/components/header/ActionButtons.tsx
  • app/(landing)/hackathons/[slug]/components/tabs/contents/Participants.tsx
  • components/hackathons/submissions/submissionCard.tsx
  • components/hackathons/submissions/submissionCard2.tsx
  • components/organization/cards/ParticipantInfo.tsx
  • components/organization/cards/ReviewSubmissionModal/SubmissionVotesTab.tsx
  • components/organization/cards/ReviewSubmissionModal/TeamSection.tsx
  • components/organization/cards/ReviewSubmissionModal/types.ts
  • components/organization/cards/TeamModal.tsx
  • components/organization/hackathons/ParticipantsTable.tsx
  • hooks/hackathon/use-register-hackathon.ts
  • hooks/use-participant-submission.ts
  • lib/api/hackathons.ts
  • types/hackathon/participant.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • components/organization/hackathons/ParticipantsTable.tsx
  • hooks/hackathon/use-register-hackathon.ts

Comment on lines +41 to +42
(p: Participant) =>
(p as any).userId === user.id || p.user?.id === user.id
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify there are no `any` escapes left in this component.
rg -nP --type=tsx '\bas\s+any\b|:\s*any\b' 'app/(landing)/hackathons/[slug]/components/header/ActionButtons.tsx'

Repository: boundlessfi/boundless

Length of output: 92


🏁 Script executed:

#!/bin/bash
# Read the file around the flagged lines
cat -n 'app/(landing)/hackathons/[slug]/components/header/ActionButtons.tsx' | head -60 | tail -30

echo "---"

# Search for 'any' in the file without type restrictions
rg -n 'as any|: any' 'app/(landing)/hackathons/[slug]/components/header/ActionButtons.tsx'

echo "---"

# Find the Participant type definition
rg -n 'type Participant|interface Participant' --no-heading

Repository: boundlessfi/boundless

Length of output: 4586


🏁 Script executed:

#!/bin/bash
# Read the Participant interface definition
cat -n 'types/hackathon/participant.ts' | sed -n '99,128p'

Repository: boundlessfi/boundless

Length of output: 879


Remove any cast in participant check.

Line 42 uses (p as any).userId, which drops type safety and is unnecessary since the Participant interface already defines userId as a direct property.

Suggested fix
-      (hackathon?.participants || []).some(
-        (p: Participant) =>
-          (p as any).userId === user.id || p.user?.id === user.id
-      )
+      (hackathon?.participants || []).some(
+        (p: Participant) => p.userId === user.id || p.user?.id === user.id
+      )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
(p: Participant) =>
(p as any).userId === user.id || p.user?.id === user.id
(hackathon?.participants || []).some(
(p: Participant) => p.userId === user.id || p.user?.id === user.id
)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/`(landing)/hackathons/[slug]/components/header/ActionButtons.tsx around
lines 41 - 42, Replace the unsafe cast in the participant identity check: in the
arrow predicate that currently uses "(p as any).userId === user.id || p.user?.id
=== user.id", remove the any cast and use the typed property "p.userId" instead
(i.e., "p.userId === user.id || p.user?.id === user.id") so the Participant type
is respected; ensure the predicate parameter remains typed as Participant and
that optional chaining on p.user?.id is preserved.

description: string;
submitterName: string;
submitterAvatar?: string;
submitterAvatar?: string | null;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add a null-avatar fallback in the header avatar UI.

Line 31 now allows null, but the current header avatar still builds url(${submitterAvatar}) unconditionally, which can render a broken avatar for missing images.

Suggested fix
-          <div
-            style={{ backgroundImage: `url(${submitterAvatar})` }}
-            className={`size-8 rounded-full border-2 bg-white bg-cover bg-center ${isPinned ? 'border-primary' : 'border-[`#2B2B2B`]'}`}
-          ></div>
+          {submitterAvatar ? (
+            <div
+              style={{ backgroundImage: `url(${submitterAvatar})` }}
+              className={`size-8 rounded-full border-2 bg-white bg-cover bg-center ${isPinned ? 'border-primary' : 'border-[`#2B2B2B`]'}`}
+            />
+          ) : (
+            <div
+              className={`flex size-8 items-center justify-center rounded-full border-2 bg-zinc-800 text-xs font-bold text-zinc-300 ${isPinned ? 'border-primary' : 'border-[`#2B2B2B`]'}`}
+            >
+              {submitterName.charAt(0).toUpperCase()}
+            </div>
+          )}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
submitterAvatar?: string | null;
{submitterAvatar ? (
<div
style={{ backgroundImage: `url(${submitterAvatar})` }}
className={`size-8 rounded-full border-2 bg-white bg-cover bg-center ${isPinned ? 'border-primary' : 'border-[`#2B2B2B`]'}`}
/>
) : (
<div
className={`flex size-8 items-center justify-center rounded-full border-2 bg-zinc-800 text-xs font-bold text-zinc-300 ${isPinned ? 'border-primary' : 'border-[`#2B2B2B`]'}`}
>
{submitterName.charAt(0).toUpperCase()}
</div>
)}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/hackathons/submissions/submissionCard.tsx` at line 31, The header
avatar currently constructs `url(${submitterAvatar})` even when
`submitterAvatar` can be null; update the component (submissionCard / the header
avatar rendering) to use a safe fallback image or initials when
`submitterAvatar` is null or empty—e.g., compute a resolvedAvatar =
submitterAvatar ?? '<fallback-url-or-empty>' (or conditionally render a
placeholder/avatar-initials) and use that value wherever
`url(${submitterAvatar})` is used so the UI never attempts to load a null URL.

name: string;
role: string;
avatar?: string;
avatar?: string | null;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check nullable avatar usage patterns in ReviewSubmissionModal components.
rg -nP --type=tsx 'src=\{comment\.author\.avatar\}' components/organization/cards/ReviewSubmissionModal
rg -nP --type=tsx 'src=\{(member|voter)\.avatar\s*(\|\||\?\?)\s*undefined\}' components/organization/cards/ReviewSubmissionModal

Repository: boundlessfi/boundless

Length of output: 120


🏁 Script executed:

#!/bin/bash
# First, explore the ReviewSubmissionModal directory structure
echo "=== Directory structure ==="
find components/organization/cards/ReviewSubmissionModal -type f | head -20

echo ""
echo "=== Search for avatar usage without type restriction ==="
rg -n 'src.*avatar' components/organization/cards/ReviewSubmissionModal

echo ""
echo "=== Search for comment.author.avatar specifically ==="
rg -n 'comment\.author\.avatar' components/organization/cards/ReviewSubmissionModal

echo ""
echo "=== Check types.ts file content ==="
cat -n components/organization/cards/ReviewSubmissionModal/types.ts

Repository: boundlessfi/boundless

Length of output: 3076


Fix avatar null-safety in SubmissionCommentsTab after type widening.

After widening the avatar fields to string | null (lines 5, 13, 24 in types.ts), SubmissionCommentsTab.tsx still passes comment.author.avatar directly to AvatarImage without normalization. Update line 41 to use comment.author.avatar || undefined to match the null-safe pattern already applied in TeamSection.tsx (line 41) and SubmissionVotesTab.tsx (line 46).

Also applies to: 13-13, 24-24

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/organization/cards/ReviewSubmissionModal/types.ts` at line 5,
SubmissionCommentsTab passes comment.author.avatar (now typed string | null)
directly to AvatarImage; make it null-safe by normalizing to undefined like the
other tabs—replace the direct prop with comment.author.avatar || undefined in
SubmissionCommentsTab (the prop passed to AvatarImage around the current line
41) so it matches the pattern used in TeamSection.tsx and
SubmissionVotesTab.tsx.

@Benjtalkshow Benjtalkshow merged commit d536c43 into boundlessfi:main Mar 16, 2026
6 of 8 checks passed
Benjtalkshow added a commit that referenced this pull request Mar 16, 2026
* fix: fix prize teirs place

* fix: fix prize teirs place

* Pr 486 (#488)

* feat: Implement comprehensive hackathon detail page with new header, sidebar, and tabbed content sections including teams, announcements, winners, participants, and resources.

* fix: update coderabit

* feat: Enhance hackathon team invitation flow, recruitment post management, and dynamic tab visibility.

* feat: Integrate AuthModalProvider for authentication handling and enhance messaging features across hackathon components

* fix: fix pagination issues in particiapants and submissions page for organizers

* fix: remove any type

---------

Co-authored-by: Collins Ikechukwu <collinschristroa@gmail.com>

* Pr 486 (#490)

* feat: Implement comprehensive hackathon detail page with new header, sidebar, and tabbed content sections including teams, announcements, winners, participants, and resources.

* fix: update coderabit

* feat: Enhance hackathon team invitation flow, recruitment post management, and dynamic tab visibility.

* feat: Integrate AuthModalProvider for authentication handling and enhance messaging features across hackathon components

* fix: fix pagination issues in particiapants and submissions page for organizers

* fix: remove any type

* fix: enhanced filtering for organizations

* fix: fix coderabbit corrections

---------

Co-authored-by: Collins Ikechukwu <collinschristroa@gmail.com>

---------

Co-authored-by: Collins Ikechukwu <collinschristroa@gmail.com>
Benjtalkshow added a commit that referenced this pull request Mar 16, 2026
* fix: fix prize teirs place

* fix: fix prize teirs place

* Pr 486 (#488)

* feat: Implement comprehensive hackathon detail page with new header, sidebar, and tabbed content sections including teams, announcements, winners, participants, and resources.

* fix: update coderabit

* feat: Enhance hackathon team invitation flow, recruitment post management, and dynamic tab visibility.

* feat: Integrate AuthModalProvider for authentication handling and enhance messaging features across hackathon components

* fix: fix pagination issues in particiapants and submissions page for organizers

* fix: remove any type

---------

Co-authored-by: Collins Ikechukwu <collinschristroa@gmail.com>

* Pr 486 (#490)

* feat: Implement comprehensive hackathon detail page with new header, sidebar, and tabbed content sections including teams, announcements, winners, participants, and resources.

* fix: update coderabit

* feat: Enhance hackathon team invitation flow, recruitment post management, and dynamic tab visibility.

* feat: Integrate AuthModalProvider for authentication handling and enhance messaging features across hackathon components

* fix: fix pagination issues in particiapants and submissions page for organizers

* fix: remove any type

* fix: enhanced filtering for organizations

* fix: fix coderabbit corrections

---------

Co-authored-by: Collins Ikechukwu <collinschristroa@gmail.com>

* fix: sync production with main changes

---------

Co-authored-by: Collins Ikechukwu <collinschristroa@gmail.com>
Benjtalkshow added a commit that referenced this pull request Mar 16, 2026
* Pr 486 (#488)

* feat: Implement comprehensive hackathon detail page with new header, sidebar, and tabbed content sections including teams, announcements, winners, participants, and resources.

* fix: update coderabit

* feat: Enhance hackathon team invitation flow, recruitment post management, and dynamic tab visibility.

* feat: Integrate AuthModalProvider for authentication handling and enhance messaging features across hackathon components

* fix: fix pagination issues in particiapants and submissions page for organizers

* fix: remove any type

---------

Co-authored-by: Collins Ikechukwu <collinschristroa@gmail.com>

* Pr 486 (#490)

* feat: Implement comprehensive hackathon detail page with new header, sidebar, and tabbed content sections including teams, announcements, winners, participants, and resources.

* fix: update coderabit

* feat: Enhance hackathon team invitation flow, recruitment post management, and dynamic tab visibility.

* feat: Integrate AuthModalProvider for authentication handling and enhance messaging features across hackathon components

* fix: fix pagination issues in particiapants and submissions page for organizers

* fix: remove any type

* fix: enhanced filtering for organizations

* fix: fix coderabbit corrections

---------

Co-authored-by: Collins Ikechukwu <collinschristroa@gmail.com>

---------

Co-authored-by: Collins Ikechukwu <collinschristroa@gmail.com>
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