Conversation
Signed-off-by: ragnep <ragneinfo@gmail.com>
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughRefactors the reputation (REP) surface from repRates to an overview/categories model with directional support (received/given), adds CIC and REP API endpoints/types, updates components and tests to use the new shapes, introduces new React Query keys/invalidation, and adds a MobileWrapperDialog maxWidthClass prop. Changes
Sequence Diagram(s)sequenceDiagram
participant User as User
participant Page as UserPageRep
participant API as API
participant Header as UserPageRepHeader
participant Mobile as UserPageRepMobile
User->>Page: Open profile / toggle direction
Page->>Page: set repDirection
alt repDirection = "received"
Page->>API: GET /profiles/{id}/rep/overview (incoming)
Page->>API: GET /profiles/{id}/rep/categories (incoming)
else repDirection = "given"
Page->>API: GET /profiles/{id}/rep/overview?direction=outgoing
Page->>API: GET /profiles/{id}/rep/categories?direction=outgoing
end
Page->>API: GET /profiles/{id}/cic/overview
API-->>Page: repOverview, repCategories, cicOverview
Page->>Page: select activeOverview / activeCategories
Page->>Header: pass overview, categories, repDirection, onRepDirectionChange
Page->>Mobile: pass overview, categories, cicOverview, repDirection, onRepDirectionChange
Header->>User: render counts, toggle
Mobile->>User: render categories, CIC avatars
User->>Header: click toggle
Header->>Page: onRepDirectionChange(direction)
Page->>Page: update state, refetch endpoints
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (6)
components/user/rep/UserPageRep.tsx (1)
40-41: Desktop and mobile views have independentrepDirectionstates.
UserPageRepmaintains its ownrepDirectionstate for desktop (passed toUserPageRepHeader), whileUserPageRepMobilecreates a separate local state. If a user switches between viewports (e.g., resizing the browser), the selected direction won't be synchronized.Consider lifting the state here and passing
repDirectionandonRepDirectionChangetoUserPageRepMobileas well for consistent UX.♻️ Proposed fix to synchronize state
<UserPageRepMobile profile={profile} repRates={repRates ?? null} initialActivityLogParams={initialActivityLogParams} + repDirection={repDirection} + onRepDirectionChange={setRepDirection} />Then update
UserPageRepMobileto accept and use these props instead of local state.Also applies to: 58-62
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/user/rep/UserPageRep.tsx` around lines 40 - 41, UserPageRep currently has a local repDirection state (useState<RepDirection>("received")) that's passed to UserPageRepHeader while UserPageRepMobile maintains its own separate state; lift repDirection and its setter (setRepDirection) in UserPageRep and pass both repDirection and an onRepDirectionChange callback (e.g., setRepDirection) down to UserPageRepMobile so both desktop and mobile use the same source of truth, then remove the local useState from UserPageRepMobile and replace its internal updates with calls to the passed onRepDirectionChange prop (also update any initial/default handling to derive from the incoming repDirection prop).components/user/rep/RepGivenList.tsx (1)
41-42: Consider if fetching 200 items at once is appropriate.The query fetches up to 200 ratings in a single request. For users with many given ratings, this could be a large payload. If this is expected to grow significantly, consider server-side pagination instead.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/user/rep/RepGivenList.tsx` around lines 41 - 42, The hardcoded query params page: "1" and page_size: "200" in RepGivenList can return an excessively large payload; modify RepGivenList to use server-side pagination instead of a fixed 200 items—replace the fixed page_size with a configurable value or state (e.g., page and pageSize in component state), fetch one page at a time, and add UI controls (next/prev or page selector) that call the API with the appropriate page and page_size; ensure the query code that currently uses page and page_size reads from those state values and handles total count/hasMore from the API to drive pagination.components/user/rep/header/UserPageRepHeader.tsx (1)
81-103: Consider simplifying the conditional rendering for Total Rep.The current structure has nested conditions that could be simplified. The "received" direction without
repRatesand the "given" direction share similar empty-state UI.💡 Simplified structure
- {repDirection === "received" && repRates ? ( + {repDirection === "received" ? ( + repRates ? ( <div className="tw-flex tw-flex-shrink-0 tw-flex-col tw-items-end tw-text-right"> <div className="tw-mb-1 tw-text-xs tw-font-semibold tw-uppercase tw-tracking-wider tw-text-iron-500"> Total Rep </div> <div className="tw-text-3xl tw-font-semibold tw-leading-none tw-tracking-tight tw-text-primary-400"> {formatNumberWithCommas(repRates.total_rep_rating)} </div> <span className="tw-mt-1 tw-text-sm tw-font-normal tw-text-iron-400"> {formatNumberWithCommas(repRates.number_of_raters)}{" "} {repRates.number_of_raters === 1 ? "rater" : "raters"} </span> </div> + ) : null ) : ( <div className="tw-flex tw-flex-shrink-0 tw-flex-col tw-items-end tw-text-right"> <div className="tw-mb-1 tw-text-xs tw-font-semibold tw-uppercase tw-tracking-wider tw-text-iron-500"> Total Rep </div> <div className="tw-text-3xl tw-font-semibold tw-leading-none tw-tracking-tight tw-text-primary-400"> - {repDirection === "received" ? "" : "—"} + — </div> </div> )}This removes the confusing
repDirection === "received" ? "" : "—"branch since the "received" case without repRates would be handled separately.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/user/rep/header/UserPageRepHeader.tsx` around lines 81 - 103, In UserPageRepHeader (component rendering Total Rep), simplify the conditional by treating only the case where repDirection === "received" && repRates shows the numeric UI (use formatNumberWithCommas(repRates.total_rep_rating) and repRates.number_of_raters), and collapse all other cases into a single fallback that renders the empty-state UI with the em dash ("—"); remove the nested ternary that checks repDirection === "received" ? "" : "—" so the "received without repRates" and "given" directions share the same fallback JSX.components/user/rep/UserPageRepMobile.tsx (1)
282-308: Direction toggle UI is duplicated fromUserPageRepHeader.The Received/Given toggle buttons (lines 282-308) have nearly identical code to
UserPageRepHeader(lines 106-131). Consider extracting a sharedRepDirectionTogglecomponent to reduce duplication.💡 Example shared component
// components/user/rep/RepDirectionToggle.tsx export default function RepDirectionToggle({ direction, onChange, size = "sm", // "sm" for mobile, "md" for desktop }: { readonly direction: RepDirection; readonly onChange: (direction: RepDirection) => void; readonly size?: "sm" | "md"; }) { const textClass = size === "sm" ? "tw-text-xs" : "tw-text-[13px]"; const iconClass = size === "sm" ? "tw-h-3 tw-w-3" : "tw-h-3.5 tw-w-3.5"; // ... shared button markup }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/user/rep/UserPageRepMobile.tsx` around lines 282 - 308, The Received/Given toggle UI in UserPageRepMobile is duplicated from UserPageRepHeader; extract a shared component (e.g., RepDirectionToggle) that accepts props { direction: RepDirection, onChange: (d: RepDirection) => void, size?: "sm" | "md" } and replace the inline button group in UserPageRepMobile with <RepDirectionToggle direction={repDirection} onChange={setRepDirection} size="sm" /> (and use size="md" where UserPageRepHeader currently renders it); ensure the new component encapsulates the conditional classes and icons (ArrowDownLeftIcon, ArrowUpRightIcon) and preserves aria-hidden and click handlers so behavior and styles remain identical.components/user/rep/RepGivenPill.tsx (1)
17-17: Zero rating is styled as negative (rose).When
rating.rating === 0, the color will betw-text-rose-500(same as negative values). If zero is meant to be neutral rather than negative, consider adjusting the condition.💡 Optional fix for neutral zero styling
- const ratingColor = rating.rating > 0 ? "tw-text-emerald-500" : "tw-text-rose-500"; + const ratingColor = + rating.rating > 0 + ? "tw-text-emerald-500" + : rating.rating < 0 + ? "tw-text-rose-500" + : "tw-text-iron-400";🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/user/rep/RepGivenPill.tsx` at line 17, The current ternary sets ratingColor so zero falls into the negative style; update the condition in RepGivenPill.tsx where ratingColor is defined (variable ratingColor, using rating.rating) to explicitly handle three states: positive (rating.rating > 0) -> "tw-text-emerald-500", negative (rating.rating < 0) -> "tw-text-rose-500", and neutral (rating.rating === 0) -> a neutral class such as "tw-text-slate-500" (or your chosen neutral token), ensuring the UI shows zero as neutral rather than negative.__tests__/components/user/rep/header/UserPageRepHeader.test.tsx (1)
17-17: Tests updated correctly, but consider adding coverage for new direction functionality.The new required props
repDirectionandonRepDirectionChangeare correctly passed to align with the updated component signature. However, both tests only coverrepDirection="received", leaving the new"given"direction and the direction toggle behavior untested.💡 Suggested additional test cases
it('calls onRepDirectionChange when direction is toggled', () => { const handleDirectionChange = jest.fn(); const repRates = { total_rep_rating: 1500, number_of_raters: 25, rating_stats: [], } as any; render( <UserPageRepHeader repRates={repRates} profile={mockProfile} repDirection="received" onRepDirectionChange={handleDirectionChange} /> ); // Add interaction to trigger direction change and verify callback }); it('renders given direction view', () => { const repRates = { total_rep_rating: 1500, number_of_raters: 25, rating_stats: [], } as any; render( <UserPageRepHeader repRates={repRates} profile={mockProfile} repDirection="given" onRepDirectionChange={() => {}} /> ); // Assert expected UI for "given" direction });Also applies to: 22-22
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@__tests__/components/user/rep/header/UserPageRepHeader.test.tsx` at line 17, Add test coverage for the new repDirection prop and toggle behavior in __tests__/components/user/rep/header/UserPageRepHeader.test.tsx: implement a test that renders UserPageRepHeader with repDirection="received" and a jest.fn() for onRepDirectionChange, simulate the user interaction that toggles direction (click the direction toggle control used by UserPageRepHeader) and assert onRepDirectionChange was called; add a second test that renders UserPageRepHeader with repDirection="given" (and a no-op onRepDirectionChange) and assert the expected "given" view is shown (check for the specific label/text or element the component renders for the given direction).
🤖 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/user/rep/RepGivenList.tsx`:
- Around line 37-39: The query key uses handle.toLowerCase() but the API call in
queryFn passes the raw handle, causing inconsistent caching; update the endpoint
in the commonApiFetch call inside queryFn to use the same normalized handle
(handle.toLowerCase()) so the query key and request use identical casing (refer
to the query key construction, queryFn, and commonApiFetch call with endpoint
`profiles/${...}/rep/ratings/by-rater`).
---
Nitpick comments:
In `@__tests__/components/user/rep/header/UserPageRepHeader.test.tsx`:
- Line 17: Add test coverage for the new repDirection prop and toggle behavior
in __tests__/components/user/rep/header/UserPageRepHeader.test.tsx: implement a
test that renders UserPageRepHeader with repDirection="received" and a jest.fn()
for onRepDirectionChange, simulate the user interaction that toggles direction
(click the direction toggle control used by UserPageRepHeader) and assert
onRepDirectionChange was called; add a second test that renders
UserPageRepHeader with repDirection="given" (and a no-op onRepDirectionChange)
and assert the expected "given" view is shown (check for the specific label/text
or element the component renders for the given direction).
In `@components/user/rep/header/UserPageRepHeader.tsx`:
- Around line 81-103: In UserPageRepHeader (component rendering Total Rep),
simplify the conditional by treating only the case where repDirection ===
"received" && repRates shows the numeric UI (use
formatNumberWithCommas(repRates.total_rep_rating) and
repRates.number_of_raters), and collapse all other cases into a single fallback
that renders the empty-state UI with the em dash ("—"); remove the nested
ternary that checks repDirection === "received" ? "" : "—" so the "received
without repRates" and "given" directions share the same fallback JSX.
In `@components/user/rep/RepGivenList.tsx`:
- Around line 41-42: The hardcoded query params page: "1" and page_size: "200"
in RepGivenList can return an excessively large payload; modify RepGivenList to
use server-side pagination instead of a fixed 200 items—replace the fixed
page_size with a configurable value or state (e.g., page and pageSize in
component state), fetch one page at a time, and add UI controls (next/prev or
page selector) that call the API with the appropriate page and page_size; ensure
the query code that currently uses page and page_size reads from those state
values and handles total count/hasMore from the API to drive pagination.
In `@components/user/rep/RepGivenPill.tsx`:
- Line 17: The current ternary sets ratingColor so zero falls into the negative
style; update the condition in RepGivenPill.tsx where ratingColor is defined
(variable ratingColor, using rating.rating) to explicitly handle three states:
positive (rating.rating > 0) -> "tw-text-emerald-500", negative (rating.rating <
0) -> "tw-text-rose-500", and neutral (rating.rating === 0) -> a neutral class
such as "tw-text-slate-500" (or your chosen neutral token), ensuring the UI
shows zero as neutral rather than negative.
In `@components/user/rep/UserPageRep.tsx`:
- Around line 40-41: UserPageRep currently has a local repDirection state
(useState<RepDirection>("received")) that's passed to UserPageRepHeader while
UserPageRepMobile maintains its own separate state; lift repDirection and its
setter (setRepDirection) in UserPageRep and pass both repDirection and an
onRepDirectionChange callback (e.g., setRepDirection) down to UserPageRepMobile
so both desktop and mobile use the same source of truth, then remove the local
useState from UserPageRepMobile and replace its internal updates with calls to
the passed onRepDirectionChange prop (also update any initial/default handling
to derive from the incoming repDirection prop).
In `@components/user/rep/UserPageRepMobile.tsx`:
- Around line 282-308: The Received/Given toggle UI in UserPageRepMobile is
duplicated from UserPageRepHeader; extract a shared component (e.g.,
RepDirectionToggle) that accepts props { direction: RepDirection, onChange: (d:
RepDirection) => void, size?: "sm" | "md" } and replace the inline button group
in UserPageRepMobile with <RepDirectionToggle direction={repDirection}
onChange={setRepDirection} size="sm" /> (and use size="md" where
UserPageRepHeader currently renders it); ensure the new component encapsulates
the conditional classes and icons (ArrowDownLeftIcon, ArrowUpRightIcon) and
preserves aria-hidden and click handlers so behavior and styles remain
identical.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
__tests__/components/user/rep/header/UserPageRepHeader.test.tsxcomponents/mobile-wrapper-dialog/MobileWrapperDialog.tsxcomponents/user/followers/UserPageFollowersModal.tsxcomponents/user/rep/RepGivenList.tsxcomponents/user/rep/RepGivenPill.tsxcomponents/user/rep/UserPageRep.tsxcomponents/user/rep/UserPageRepMobile.tsxcomponents/user/rep/header/UserPageRepHeader.tsxcomponents/user/rep/new-rep/GrantRepDialog.tsx
Signed-off-by: ragnep <ragneinfo@gmail.com>
Signed-off-by: ragnep <ragneinfo@gmail.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (6)
components/user/rep/header/UserPageRepHeader.tsx (1)
102-127: Consider addingaria-pressedfor toggle button accessibility.The direction toggle buttons act as a toggle group. Adding
aria-pressedwould improve screen reader experience:♿ Suggested accessibility improvement
<button type="button" onClick={() => onRepDirectionChange("received")} + aria-pressed={repDirection === "received"} className={`tw-inline-flex tw-cursor-pointer tw-items-center tw-gap-1.5 tw-border-0 tw-bg-transparent tw-p-0 tw-text-[13px] tw-font-medium tw-transition-colors tw-duration-200 ${ repDirection === "received" ? "tw-text-iron-100" : "tw-text-iron-500 hover:tw-text-iron-300" }`} >Apply similarly to the "Given" button.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/user/rep/header/UserPageRepHeader.tsx` around lines 102 - 127, In UserPageRepHeader (buttons using ArrowDownLeftIcon/ArrowUpRightIcon and onRepDirectionChange), add aria-pressed to each toggle button so screen readers know which is active: set aria-pressed={repDirection === "received"} on the "Received" button and aria-pressed={repDirection === "given"} on the "Given" button (keep existing onClick and class logic unchanged); this makes the repDirection state exposed as a pressed toggle for accessibility.components/user/rep/UserPageRepMobile.tsx (1)
268-294: Consider extracting the direction toggle into a shared component.The direction toggle UI (Received/Given buttons with icons) is duplicated between
UserPageRepMobileandUserPageRepHeader. This could be extracted into a reusableRepDirectionTogglecomponent to reduce duplication and ensure consistent styling.♻️ Example extraction
// components/user/rep/RepDirectionToggle.tsx export function RepDirectionToggle({ direction, onChange, size = "md", // "sm" for mobile, "md" for desktop }: { direction: RepDirection; onChange: (direction: RepDirection) => void; size?: "sm" | "md"; }) { const textSize = size === "sm" ? "tw-text-xs" : "tw-text-[13px]"; const iconSize = size === "sm" ? "tw-h-3 tw-w-3" : "tw-h-3.5 tw-w-3.5"; // ... shared button rendering }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/user/rep/UserPageRepMobile.tsx` around lines 268 - 294, Duplicate Received/Given toggle UI in UserPageRepMobile and UserPageRepHeader should be extracted into a reusable component; create a new RepDirectionToggle component that accepts props { direction: RepDirection, onChange: (d: RepDirection) => void, size?: "sm" | "md" } and implement the shared rendering (buttons, icons, active/inactive classes) with conditional classes based on size, then replace the inline toggle in UserPageRepMobile and UserPageRepHeader by rendering <RepDirectionToggle direction={repDirection} onChange={onRepDirectionChange} size="sm" /> (or size="md" in the header) to remove duplication and keep styling consistent.components/user/rep/RepCategoryPill.tsx (1)
78-88: Consider explicit null check forauthenticated_user_contribution.Using
!!category.authenticated_user_contributiontreats bothnulland0as falsy. If the API returns0for an explicit zero contribution (vsnullfor unauthenticated/no relationship), this block won't render "My Rate: 0".If
0is semantically equivalent to "no contribution to display," the current logic is correct. Otherwise, consider:- {!!category.authenticated_user_contribution && ( + {category.authenticated_user_contribution !== null && (🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/user/rep/RepCategoryPill.tsx` around lines 78 - 88, The conditional currently uses a truthy check on category.authenticated_user_contribution which hides a valid 0 value; change the check in RepCategoryPill (the conditional around category.authenticated_user_contribution) to an explicit null/undefined test (e.g., use category.authenticated_user_contribution != null or check !== null && !== undefined) so that 0 renders as "My Rate: 0" while still hiding null/undefined.openapi.yaml (3)
9618-9621: Add enum descriptions forApiRepDirectionsemantics.A short description mapping
incoming/outgoingto product language (e.g., received/given) would reduce interpretation mistakes for API consumers.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@openapi.yaml` around lines 9618 - 9621, Update the ApiRepDirection schema to include clear descriptions mapping the enum values to product language: add a top-level description for ApiRepDirection and per-enum value descriptions clarifying that "incoming" means a received report (e.g., from user/customer) and "outgoing" means a given/sent report (e.g., from system/team). Edit the ApiRepDirection definition (referencing the enum with values "incoming" and "outgoing") to include these descriptive fields so API consumers can unambiguously interpret each value.
5056-5071: Clarifymin_price/max_priceinteraction rules in the contract.Please document behavior for
min_price > max_priceand confirm whether these filters are ignored unlesssort=PRICE(or rejected with400).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@openapi.yaml` around lines 5056 - 5071, Update the OpenAPI contract to explicitly state the interaction rules for min_price and max_price: clarify that both filters are only considered when sort=PRICE (or explicitly state if they apply regardless), and define the behavior when min_price > max_price (either return HTTP 400 with a validation error or ignore the filters); add this policy to the description for the parameters min_price and max_price and document the corresponding 400 response schema and example for the endpoint that accepts sort=PRICE so clients know the server behavior and error format.
2913-2930: Consider documenting explicit defaults fordirection,page, andpage_size.These new REP endpoints expose optional pagination/direction controls, but defaults are not explicit in the contract. Adding defaults improves client predictability and caching behavior.
Also applies to: 2952-2976, 3006-3023
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@openapi.yaml` around lines 2913 - 2930, The OpenAPI parameters direction, page, and page_size are optional but lack explicit default values; update the parameter definitions for direction (referring to ApiRepDirection), page, and page_size to include a default property (e.g., direction: "forward" or other domain-appropriate enum member, page: 1, page_size: 20) so clients and caches have predictable behavior; apply the same default additions to the other occurrences noted (the blocks around lines referenced in the review) by adding default fields to each parameter object and, if needed, document the chosen defaults in the ApiRepDirection schema description.
🤖 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/react-query-wrapper/ReactQueryWrapper.tsx`:
- Around line 109-111: The new QueryKey values (QueryKey.REP_OVERVIEW,
QueryKey.REP_CATEGORIES, QueryKey.CIC_OVERVIEW) are being invalidated in
onProfileCICModify, onProfileRepModify, and onIdentityBulkRate but no queries
use those keys; either implement query hooks that use these keys or remove the
invalidation calls. Add three hooks (for example useRepOverviewQuery,
useRepCategoriesQuery, useCicOverviewQuery) that call useQuery/useInfiniteQuery
with the exact QueryKey constants and the appropriate fetcher/data-shape so
invalidations take effect; alternatively delete or stop calling the invalidation
logic in onProfileCICModify, onProfileRepModify, and onIdentityBulkRate if those
queries aren’t needed. Ensure the hook names and the QueryKey enum values are
referenced verbatim so the keys match the invalidation calls.
---
Nitpick comments:
In `@components/user/rep/header/UserPageRepHeader.tsx`:
- Around line 102-127: In UserPageRepHeader (buttons using
ArrowDownLeftIcon/ArrowUpRightIcon and onRepDirectionChange), add aria-pressed
to each toggle button so screen readers know which is active: set
aria-pressed={repDirection === "received"} on the "Received" button and
aria-pressed={repDirection === "given"} on the "Given" button (keep existing
onClick and class logic unchanged); this makes the repDirection state exposed as
a pressed toggle for accessibility.
In `@components/user/rep/RepCategoryPill.tsx`:
- Around line 78-88: The conditional currently uses a truthy check on
category.authenticated_user_contribution which hides a valid 0 value; change the
check in RepCategoryPill (the conditional around
category.authenticated_user_contribution) to an explicit null/undefined test
(e.g., use category.authenticated_user_contribution != null or check !== null &&
!== undefined) so that 0 renders as "My Rate: 0" while still hiding
null/undefined.
In `@components/user/rep/UserPageRepMobile.tsx`:
- Around line 268-294: Duplicate Received/Given toggle UI in UserPageRepMobile
and UserPageRepHeader should be extracted into a reusable component; create a
new RepDirectionToggle component that accepts props { direction: RepDirection,
onChange: (d: RepDirection) => void, size?: "sm" | "md" } and implement the
shared rendering (buttons, icons, active/inactive classes) with conditional
classes based on size, then replace the inline toggle in UserPageRepMobile and
UserPageRepHeader by rendering <RepDirectionToggle direction={repDirection}
onChange={onRepDirectionChange} size="sm" /> (or size="md" in the header) to
remove duplication and keep styling consistent.
In `@openapi.yaml`:
- Around line 9618-9621: Update the ApiRepDirection schema to include clear
descriptions mapping the enum values to product language: add a top-level
description for ApiRepDirection and per-enum value descriptions clarifying that
"incoming" means a received report (e.g., from user/customer) and "outgoing"
means a given/sent report (e.g., from system/team). Edit the ApiRepDirection
definition (referencing the enum with values "incoming" and "outgoing") to
include these descriptive fields so API consumers can unambiguously interpret
each value.
- Around line 5056-5071: Update the OpenAPI contract to explicitly state the
interaction rules for min_price and max_price: clarify that both filters are
only considered when sort=PRICE (or explicitly state if they apply regardless),
and define the behavior when min_price > max_price (either return HTTP 400 with
a validation error or ignore the filters); add this policy to the description
for the parameters min_price and max_price and document the corresponding 400
response schema and example for the endpoint that accepts sort=PRICE so clients
know the server behavior and error format.
- Around line 2913-2930: The OpenAPI parameters direction, page, and page_size
are optional but lack explicit default values; update the parameter definitions
for direction (referring to ApiRepDirection), page, and page_size to include a
default property (e.g., direction: "forward" or other domain-appropriate enum
member, page: 1, page_size: 20) so clients and caches have predictable behavior;
apply the same default additions to the other occurrences noted (the blocks
around lines referenced in the review) by adding default fields to each
parameter object and, if needed, document the chosen defaults in the
ApiRepDirection schema description.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (10)
generated/models/ApiCicContributor.tsis excluded by!**/generated/**generated/models/ApiCicContributorsPage.tsis excluded by!**/generated/**generated/models/ApiCicOverview.tsis excluded by!**/generated/**generated/models/ApiRepCategoriesPage.tsis excluded by!**/generated/**generated/models/ApiRepCategory.tsis excluded by!**/generated/**generated/models/ApiRepContributor.tsis excluded by!**/generated/**generated/models/ApiRepContributorsPage.tsis excluded by!**/generated/**generated/models/ApiRepDirection.tsis excluded by!**/generated/**generated/models/ApiRepOverview.tsis excluded by!**/generated/**generated/models/ObjectSerializer.tsis excluded by!**/generated/**
📒 Files selected for processing (10)
components/react-query-wrapper/ReactQueryWrapper.tsxcomponents/user/rep/RepCategoryPill.tsxcomponents/user/rep/UserPageRep.helpers.tscomponents/user/rep/UserPageRep.tsxcomponents/user/rep/UserPageRepMobile.tsxcomponents/user/rep/header/UserPageRepHeader.tsxcomponents/user/rep/new-rep/GrantRepDialog.tsxcomponents/user/rep/new-rep/UserPageRepNewRep.tsxcomponents/user/rep/new-rep/UserPageRepNewRepSearch.tsxopenapi.yaml
💤 Files with no reviewable changes (1)
- components/user/rep/UserPageRep.helpers.ts
There was a problem hiding this comment.
🧹 Nitpick comments (2)
__tests__/components/user/rep/header/UserPageRepHeader.test.tsx (1)
22-24: Strengthen the null-overview test assertion.Line 24 only checks for
'Rep', which is too broad and may still pass when the fallback UI is broken. Assert the explicit null-state output instead.Suggested test tightening
it('renders without overview', () => { - const { container } = render(<UserPageRepHeader overview={null} categories={[]} profile={mockProfile} repDirection="received" onRepDirectionChange={() => {}} />); - expect(container).toHaveTextContent('Rep'); + render(<UserPageRepHeader overview={null} categories={[]} profile={mockProfile} repDirection="received" onRepDirectionChange={() => {}} />); + expect(screen.getByText('Total Rep')).toBeInTheDocument(); + expect(screen.getByText('—')).toBeInTheDocument(); + expect(screen.queryByText('1,500')).not.toBeInTheDocument(); });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@__tests__/components/user/rep/header/UserPageRepHeader.test.tsx` around lines 22 - 24, The current test 'renders without overview' only checks for the broad text 'Rep' and can pass even if the null-state UI breaks; update the test that renders <UserPageRepHeader overview={null} ...> to assert the explicit fallback output shown when overview is null—locate the null-state fallback string or test-id in the UserPageRepHeader component (e.g., the fallback message or an element like 'rep-overview-empty') and replace the generic expect(container).toHaveTextContent('Rep') with a precise assertion (e.g., getByText/finding the exact fallback text or expect(queryByTestId('rep-overview-empty')).toBeInTheDocument()) so the test verifies the real null-state UI.components/user/rep/UserPageRep.tsx (1)
55-60: Extract shared categories pagination params to avoid drift.The same
page/page_size/top_contributors_limitblock is duplicated. A shared constant would reduce maintenance risk.♻️ Suggested refactor
+const REP_CATEGORIES_BASE_PARAMS = { + page: "1", + page_size: "20", + top_contributors_limit: "5", +} as const; + const { data: repCategories } = useQuery<ApiRepCategoriesPage>({ @@ - params: { - page: "1", - page_size: "20", - top_contributors_limit: "5", - }, + params: REP_CATEGORIES_BASE_PARAMS, @@ const { data: repCategoriesGiven } = useQuery<ApiRepCategoriesPage>({ @@ - params: { - direction: "outgoing", - page: "1", - page_size: "20", - top_contributors_limit: "5", - }, + params: { + ...REP_CATEGORIES_BASE_PARAMS, + direction: "outgoing", + },Also applies to: 87-92
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/user/rep/UserPageRep.tsx` around lines 55 - 60, Extract the duplicated params object into a shared constant (e.g., CATEGORY_PAGINATION_PARAMS = { page: "1", page_size: "20", top_contributors_limit: "5" }) and replace the inline params in both requests that use endpoint `profiles/${user}/rep/categories` (the params blocks at the two call sites) with that constant; ensure imports/exports or file-scope placement keeps the constant visible to both call sites and preserves immutability (use const or Object.freeze if needed).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@__tests__/components/user/rep/header/UserPageRepHeader.test.tsx`:
- Around line 22-24: The current test 'renders without overview' only checks for
the broad text 'Rep' and can pass even if the null-state UI breaks; update the
test that renders <UserPageRepHeader overview={null} ...> to assert the explicit
fallback output shown when overview is null—locate the null-state fallback
string or test-id in the UserPageRepHeader component (e.g., the fallback message
or an element like 'rep-overview-empty') and replace the generic
expect(container).toHaveTextContent('Rep') with a precise assertion (e.g.,
getByText/finding the exact fallback text or
expect(queryByTestId('rep-overview-empty')).toBeInTheDocument()) so the test
verifies the real null-state UI.
In `@components/user/rep/UserPageRep.tsx`:
- Around line 55-60: Extract the duplicated params object into a shared constant
(e.g., CATEGORY_PAGINATION_PARAMS = { page: "1", page_size: "20",
top_contributors_limit: "5" }) and replace the inline params in both requests
that use endpoint `profiles/${user}/rep/categories` (the params blocks at the
two call sites) with that constant; ensure imports/exports or file-scope
placement keeps the constant visible to both call sites and preserves
immutability (use const or Object.freeze if needed).
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
__tests__/components/user/rep/header/UserPageRepHeader.test.tsxcomponents/user/rep/UserPageRep.tsx
Signed-off-by: ragnep <ragneinfo@gmail.com>
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
components/user/rep/UserPageRepMobile.tsx (1)
338-370:⚠️ Potential issue | 🟡 MinorKeep REP edit flow direction-safe when switching tabs.
Editing is gated to
receivedin pills/CTA, but the modal render path (Line 471) is not direction-gated, so a previously selected category can persist across a direction change.Suggested fix
+ useEffect(() => { + if (repDirection !== "received") { + setEditCategory(null); + } + }, [repDirection]); ... - {canEditRep && editCategory && ( + {canEditRep && repDirection === "received" && editCategory && ( <UserPageRepModifyModal profile={profile} category={editCategory} onClose={() => setEditCategory(null)} /> )}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/user/rep/UserPageRepMobile.tsx` around lines 338 - 370, The rep "Grant" flow isn't direction-safe: while the CTA is only shown when repDirection === "received", the modal render path isn't gated so a previously-selected category can persist after switching tabs; fix by gating the modal render and/or clearing its state on direction change — ensure the same repDirection check used for the button (repDirection === "received" && canEditRep) is applied before rendering the grant-rep modal (the isGrantRepOpen modal) and/or reset modal-related state (e.g., selectedCategory or isGrantRepOpen via setIsGrantRepOpen) inside the repDirection change handler or an effect so the modal cannot open with stale data when the direction switches.
🧹 Nitpick comments (1)
components/user/rep/UserPageRep.tsx (1)
47-49: Prefer explicit"incoming"direction in request params for symmetry and safety.These queries key on incoming direction but rely on backend default behavior. Sending
direction: "incoming"explicitly avoids accidental behavior drift if API defaults change later.♻️ Proposed patch
queryFn: async () => await commonApiFetch<ApiRepOverview>({ endpoint: `profiles/${user}/rep/overview`, + params: { direction: "incoming" }, }), @@ queryFn: async () => await commonApiFetch<ApiRepCategoriesPage>({ endpoint: `profiles/${user}/rep/categories`, params: { + direction: "incoming", page: "1", page_size: "20", top_contributors_limit: "5", }, }),Also applies to: 60-67
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/user/rep/UserPageRep.tsx` around lines 47 - 49, The request to fetch reputation overview uses commonApiFetch for endpoint `profiles/${user}/rep/overview` but relies on backend defaults for direction; update the call(s) to pass an explicit request param `{ direction: "incoming" }` (i.e., include direction: "incoming" in the params/options passed to commonApiFetch) so both the overview fetch and the other rep query call that uses commonApiFetch explicitly specify incoming direction to avoid relying on server-side defaults.
🤖 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/user/rep/header/UserPageRepHeader.tsx`:
- Around line 111-140: The two buttons used to toggle repDirection ("received"
and "given") are visually stateful but don't expose their pressed state to
assistive tech; update both button elements that call onRepDirectionChange and
check repDirection to include an aria-pressed attribute set to true when
repDirection matches the button's value (e.g., repDirection === "received" for
the Received button, repDirection === "given" for the Given button) so screen
readers can detect the selected state while keeping existing className logic and
icons (ArrowDownLeftIcon, ArrowUpRightIcon) intact.
In `@components/user/rep/RepCategoryPill.tsx`:
- Around line 58-69: The RepCategoryPill component is nesting interactive
content (OverlappingAvatars, which can render Link anchors) inside a clickable
button when canEdit is true; move the avatar block out of the button so the
button and OverlappingAvatars are siblings (or render OverlappingAvatars in a
non-interactive mode if such a prop exists). Locate the JSX that renders
OverlappingAvatars (the span using stopPropagation and the onItemClick handler)
and extract it so it sits outside the button rendered by RepCategoryPill,
keeping the stopPropagation logic on the avatar click handlers to prevent parent
clicks, and ensure keyboard focus/aria behavior remains correct after the
change.
- Around line 30-31: Currently href is only set when c.profile.handle exists,
causing links to be missing for contributors who only have a wallet; update the
spread so href uses the same handle-or-wallet fallback as ariaLabel (e.g. set
href to `/${c.profile.handle ?? c.profile.primary_address}`) while preserving
ariaLabel as `c.profile.handle ?? c.profile.primary_address` in
RepCategoryPill.tsx so all contributors get a valid link.
In `@components/user/rep/UserPageRepMobile.tsx`:
- Around line 263-292: The Received/Given toggle buttons are visually stateful
but lack semantic state for assistive tech; update the two button elements that
call onRepDirectionChange and read repDirection to include an aria-pressed
attribute (e.g., aria-pressed={repDirection === "received"} for the "Received"
button and aria-pressed={repDirection === "given"} for the "Given" button) so
screen readers can detect the selected state; also ensure each button has a
clear accessible name (the existing text labels are fine) and keep the existing
onClick handlers and classes unchanged.
---
Outside diff comments:
In `@components/user/rep/UserPageRepMobile.tsx`:
- Around line 338-370: The rep "Grant" flow isn't direction-safe: while the CTA
is only shown when repDirection === "received", the modal render path isn't
gated so a previously-selected category can persist after switching tabs; fix by
gating the modal render and/or clearing its state on direction change — ensure
the same repDirection check used for the button (repDirection === "received" &&
canEditRep) is applied before rendering the grant-rep modal (the isGrantRepOpen
modal) and/or reset modal-related state (e.g., selectedCategory or
isGrantRepOpen via setIsGrantRepOpen) inside the repDirection change handler or
an effect so the modal cannot open with stale data when the direction switches.
---
Nitpick comments:
In `@components/user/rep/UserPageRep.tsx`:
- Around line 47-49: The request to fetch reputation overview uses
commonApiFetch for endpoint `profiles/${user}/rep/overview` but relies on
backend defaults for direction; update the call(s) to pass an explicit request
param `{ direction: "incoming" }` (i.e., include direction: "incoming" in the
params/options passed to commonApiFetch) so both the overview fetch and the
other rep query call that uses commonApiFetch explicitly specify incoming
direction to avoid relying on server-side defaults.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (10)
__tests__/components/user/identity/header/UserPageIdentityHeaderCIC.test.tsx__tests__/components/user/rep/header/UserPageRepHeader.test.tsxcomponents/user/identity/header/UserPageIdentityHeader.tsxcomponents/user/identity/header/UserPageIdentityHeaderCIC.tsxcomponents/user/rep/RepCategoryPill.tsxcomponents/user/rep/UserPageRep.helpers.tscomponents/user/rep/UserPageRep.tsxcomponents/user/rep/UserPageRepMobile.tsxcomponents/user/rep/header/TopRaterAvatars.tsxcomponents/user/rep/header/UserPageRepHeader.tsx
💤 Files with no reviewable changes (1)
- components/user/rep/header/TopRaterAvatars.tsx
|


Summary by CodeRabbit
New Features
Bug Fixes