Conversation
Signed-off-by: prxt6529 <prxt@6529.io>
Signed-off-by: prxt6529 <prxt@6529.io>
|
Important Review skippedReview was skipped as selected files did not have any reviewable changes. 💤 Files selected but had no reviewable changes (1)
⛔ Files ignored due to path filters (1)
You can disable this status message by setting the WalkthroughRefactors subscription and distribution UIs: replaces legacy subscription types with generated models, adds edition-preference toggle, reworks top‑up and modal flows (confirm token, upload photos, automatic airdrops) with context wiring, adds helpers/constants, updates styles, and expands tests. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Admin as Admin/User
participant UI as Review UI (Footer)
participant Ctx as DistributionPlanToolContext
participant Modal as Confirm/Upload Modal
participant API as Server (commonApi)
Admin->>UI: trigger admin action (Finalize / Upload Photos / Automatic Airdrops)
UI->>Ctx: read confirmedTokenId
alt confirmedTokenId missing
UI->>Modal: open ConfirmTokenIdModal
Modal->>Admin: prompt for tokenId
Admin->>Modal: submit tokenId
Modal->>Ctx: setConfirmedTokenId(tokenId)
end
UI->>Modal: open Upload/AutomaticAirdrops modal
Modal->>Admin: accept file/CSV + tokenId
Admin->>Modal: submit payload
Modal->>API: POST upload/normalize(contract, tokenId, payload)
API-->>Modal: response (success/error)
Modal->>UI: close and update state
UI->>Admin: show toast and refresh overview
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60–90 minutes
Possibly related PRs
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
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: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
components/user/subscriptions/UserPageSubscriptionsMode.tsx (1)
33-82: Critical: Toggle state is not persisted to the backend.The
toggleModefunction only updates local state (lines 38-40) without making the API call to persist changes. This means the toggle will appear to work in the UI but the setting won't be saved, causing data loss when the page refreshes or the component remounts.The commented-out API logic (lines 41-81) shows the intended implementation. Do you want me to help uncomment and verify this logic, or is there a tracking issue for implementing this TODO?
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
components/user/subscriptions/UserPageSubscriptionsMode.tsx(3 hunks)components/user/subscriptions/UserPageSubscriptionsTopUp.tsx(1 hunks)styles/globals.scss(6 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
components/user/subscriptions/UserPageSubscriptionsMode.tsx (1)
components/distribution-plan-tool/common/CircleLoader.tsx (1)
CircleLoader(9-39)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (6)
components/user/subscriptions/UserPageSubscriptionsTopUp.tsx (1)
143-144: LGTM! Formatting change with no functional impact.The Link attributes are now on separate lines, improving readability while maintaining the same behavior.
components/user/subscriptions/UserPageSubscriptionsMode.tsx (3)
4-6: LGTM! CircleLoader import added for loading indicators.The CircleLoader component and size enum are properly imported to replace the previous spinner implementation.
25-27: LGTM! State variables added for Edition Preference toggle.The new state variables properly track the edition preference mode and its loading state.
131-172: LGTM! Edition Preference section properly gated behind Automatic mode.The conditional rendering ensures the Edition Preference controls only appear when Automatic mode is active, which aligns with the intended two-tier UI design.
styles/globals.scss (2)
22-25: LGTM! Formatting improvement for layout calculation.The
--layout-margincalculation is reformatted to multi-line for better readability while maintaining the same computed value.
687-697: LGTM! New visual states added for toggle components.The hover and checked states for
.react-toggleenhance the UI feedback for the new two-tier toggle system introduced in UserPageSubscriptionsMode.tsx.
Signed-off-by: prxt6529 <prxt@6529.io>
Signed-off-by: prxt6529 <prxt@6529.io>
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (5)
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx (2)
307-311: UseNumber.parseIntfor consistency.Per static analysis, prefer
Number.parseIntover the globalparseInt.onChange={(e) => { - const value = parseInt(e.target.value, 10); + const value = Number.parseInt(e.target.value, 10); setSelectedCount(value); handleUpdateSubscriptionCount(value); }}
298-321: Consider the UX of immediate API calls on selection change.Every dropdown selection triggers an API call immediately. If users accidentally select the wrong value, they'll experience unnecessary network requests and potential brief UI flickers. Consider adding a confirmation button or debouncing, though this may be acceptable depending on UX requirements.
components/user/subscriptions/UserPageSubscriptionsEditionPreference.tsx (3)
27-35: SimplifysubscriptionEligibilityCountcomputation and confirm default semanticsTwo points here:
useMemois unnecessary for such a cheap derivation; a simple computed constant per render is enough and clearer:- const subscriptionEligibilityCount = useMemo<number>( - () => props.details?.subscription_eligibility_count ?? 1, - [props.details?.subscription_eligibility_count] - ); + const subscriptionEligibilityCount = + props.details?.subscription_eligibility_count ?? 1;
- Please confirm that falling back to
1whendetailsisundefined(or the field is missing) is intentional and matches backend semantics; in some flows,0or omitting the badge entirely might be more accurate.
32-35: Effect dependency choice is acceptable but relies ondetailsidentity
useEffectdepends onprops.detailsand resetsisAllEditionsfromprops.details?.subscribe_all_editions. This is fine if upstream always provides a newdetailsobject when data changes (typical for API responses). If upstream mutates an existing object in place, this effect would not re-run whensubscribe_all_editionsflips.If you expect in-place mutation, consider depending on the specific field instead:
- useEffect(() => { - setIsAllEditions(props.details?.subscribe_all_editions ?? false); - }, [props.details]); + useEffect(() => { + setIsAllEditions(props.details?.subscribe_all_editions ?? false); + }, [props.details?.subscribe_all_editions]);Otherwise current approach is fine.
82-127: Toggle UI is clear; consider small UX/a11y refinementsThe layout, readonly handling, and loader placement are all good. A couple of optional polish points:
Accessible labeling: With two
<label>elements pointing to the sameid, some screen readers may read both texts as the control’s name (e.g., “One edition All eligible”). You might consider:
- Using a single label as the accessible name, or
- Adding an explicit
aria-label/aria-labelledbyto control exactly what’s announced.Helper text association: The explanatory line (“You will receive…”) could be associated with the toggle via
aria-describedbyfor better screen-reader context if you want to go the extra mile.These are not blockers but would tighten accessibility.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
components/user/subscriptions/UserPageSubscriptions.tsx(2 hunks)components/user/subscriptions/UserPageSubscriptionsEditionPreference.tsx(1 hunks)components/user/subscriptions/UserPageSubscriptionsMode.tsx(3 hunks)components/user/subscriptions/UserPageSubscriptionsTopUp.tsx(2 hunks)components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx(6 hunks)entities/ISubscription.ts(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
components/user/subscriptions/UserPageSubscriptionsMode.tsx (1)
components/distribution-plan-tool/common/CircleLoader.tsx (1)
CircleLoader(9-39)
components/user/subscriptions/UserPageSubscriptions.tsx (2)
components/user/subscriptions/UserPageSubscriptionsAirdropAddress.tsx (1)
UserPageSubscriptionsAirdropAddress(17-67)components/user/subscriptions/UserPageSubscriptionsEditionPreference.tsx (1)
UserPageSubscriptionsEditionPreference(13-129)
🪛 GitHub Check: SonarCloud Code Analysis
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx
[warning] 196-196: Change the argument of this setter to not use its matching state variable
[warning] 308-308: Prefer Number.parseInt over parseInt.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (11)
components/user/subscriptions/UserPageSubscriptionsTopUp.tsx (2)
143-144: LGTM!The attribute formatting change is purely cosmetic and maintains correct behavior.
256-263: LGTM!The
tw-text-iron-400class addition provides consistent styling for the "Show More" button section.entities/ISubscription.ts (2)
6-7: Verify API backward compatibility for new required fields.The new fields
subscribe_all_editionsandsubscription_eligibility_countare non-optional. Ensure all API responses always include these fields to prevent runtime errors. If the backend might omit them for existing data, consider making them optional with?.
23-23: Same consideration forsubscribed_count.Verify the API always returns this field. The consuming code in
UserPageSubscriptionsUpcoming.tsx(line 132) handles the fallback with?? 1, suggesting it might be optional. If so, mark it assubscribed_count?: number.components/user/subscriptions/UserPageSubscriptionsMode.tsx (2)
84-97: LGTM!The timestamp display logic correctly guards against undefined/zero values and uses consistent UTC formatting with clear locale options.
101-117: LGTM!The layout migration to Tailwind utility classes is clean. The
CircleLoaderprovides a better loading indicator thanDotLoaderand is correctly sized withCircleLoaderSize.MEDIUM.components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx (2)
131-140: LGTM!The
subscribedCountmemoization andselectedCountstate synchronization are correctly implemented, with proper fallback to1when the API field is missing.
207-253: LGTM!The
handleUpdateSubscriptionCountfunction follows the established patterns: proper auth check, optimistic updates with rollback on failure, and toast notifications. Error handling correctly revertsselectedCounttosubscribedCount.components/user/subscriptions/UserPageSubscriptions.tsx (2)
26-26: LGTM!The import for the new
UserPageSubscriptionsEditionPreferencecomponent is correctly added.
303-308: LGTM!The
UserPageSubscriptionsEditionPreferencecomponent is correctly integrated with the required props (profileKey,details,readonly,refresh) matching its signature from the relevant code snippets.components/user/subscriptions/UserPageSubscriptionsEditionPreference.tsx (1)
1-25: Component structure and context usage look solidThe client component wiring (props typing,
AuthContextusage, and initial local state) is consistent and clear; no issues with the basic structure or imports.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (6)
components/user/subscriptions/UserPageSubscriptionsEditionPreference.tsx (1)
79-125: LGTM! Clean UI implementation with proper accessibility.The rendering logic is well-structured with:
- Proper label associations via
htmlFor- Conditional loading indicator
- Appropriate disabled states
- Clear user guidance text
The inline
SubscribeAllEditionsBodyinterface (lines 45-47) could optionally be extracted to a types file or module-level constant for potential reuse, but the current placement is acceptable for a simple, single-use type.components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx (5)
78-80: Eligibility count threading looks good; consider defensive normalization.Passing
subscription_eligibility_count ?? 1through aseligibilityCountand using it to drive the dropdown makes sense. To be a bit more robust against unexpected backend values (0, negative, NaN) before using it as an array length and in the/ Ndisplay, consider normalizing insideSubscriptionRowto a minimum positive integer and using that normalized value everywhere.Also applies to: 116-116
132-141: State sync betweensubscribed_countandselectedCountis correct;useMemois optional.The combination of
subscribedCountplus theuseEffectthat resyncsselectedCounton prop changes correctly keeps the control aligned with refreshed subscription data. Sincesubscribed_countis already a primitive, you could simplify slightly by computingconst subscribedCount = props.subscription.subscribed_count ?? 1;withoutuseMemo, but the current version is functionally fine.
196-204:submiterror handling is now sound; you can drop the redundantreturnin the catch.Removing the previous
setSubscribed(subscribed)no‑op resolves the earlier concern, andfinallynow guaranteesisSubmittingis reset on every path. The explicitreturn;in thecatchblock is redundant because there’s no code after thetry/catch/finally; you can omit it to tighten the control flow.
207-252:handleUpdateSubscriptionCountis well‑structured; add simple guards and reuse the body type.The mutation flow here is solid: you guard on
isSubmitting/minting_today, re‑check auth, optimistically updateselectedCountwith a clean revert tosubscribedCounton failure, and always clearisSubmittingwhile surfacing success/error toasts and callingrefresh. Two small follow‑ups worth considering:
- Validate
valuebefore posting (e.g., early‑return if it’s< 1or> props.eligibilityCount) so this stays safe even if the caller or UI changes later.- Hoist
UpdateSubscriptionCountBody(andSubscribeBodyabove) to shared type aliases outside the component so they aren’t redefined on every render and can be reused across related subscription calls.It would also be good to add tests covering both the success and failure paths here to keep this file above the required coverage threshold.
284-327: Action cluster + quantity selector work well; add an explicit label for the<select>.The right‑hand cluster (spinner, toggle, and conditional quantity selector) respects
readonly,isSubmitting,minting_today, andeligibilityCountcorrectly, and the optimistic update behavior is consistent with the handler. For accessibility, the new<select>currently has no explicit label, which makes its purpose unclear to screen‑reader users; adding anaria-labelsuch asSubscribed quantity for ${props.title} #${props.subscription.token_id}(or wiring it to visible text viaaria-labelledby) would address that without changing behavior.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
components/user/subscriptions/UserPageSubscriptionsEditionPreference.tsx(1 hunks)components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx(6 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx,js,jsx}: Do not include any comments in the code; it should be self-explanatory
Write correct, up-to-date, bug-free, fully componentized, secure, and efficient code
Include all required imports and ensure proper naming of key components
Use NextJS features that match the current version
**/*.{ts,tsx,js,jsx}: Enforce ≥ 80% line coverage for files changed sincemainvianpm run test
All code must pass ESLint (Next's Core Web Vitals + React Hooks rules) vianpm run lint
Use<Link>from Next.js for internal navigation instead of<a>tags or HTML anchors
Use<Image>fromnext/imageinstead of HTML<img>elements
Files:
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsxcomponents/user/subscriptions/UserPageSubscriptionsEditionPreference.tsx
**/*.{tsx,jsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{tsx,jsx}: Use FontAwesome for icons in React components
Use TailwindCSS for styling in React components
Use react-query for data fetching
Always addreadonlybefore props in React components
Files:
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsxcomponents/user/subscriptions/UserPageSubscriptionsEditionPreference.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: All code must pass TypeScript type checking vianpm run type-check(tsc --noEmit)
Use'use cache'directive at the top of Server Components or functions to explicitly opt-in to caching in Next.js 16
Files:
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsxcomponents/user/subscriptions/UserPageSubscriptionsEditionPreference.tsx
🧬 Code graph analysis (2)
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx (1)
services/api/common-api.ts (1)
commonApiPost(230-249)
components/user/subscriptions/UserPageSubscriptionsEditionPreference.tsx (3)
entities/ISubscription.ts (1)
SubscriptionDetails(1-8)components/auth/Auth.tsx (1)
AuthContext(83-93)components/distribution-plan-tool/common/CircleLoader.tsx (1)
CircleLoader(9-39)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (5)
components/user/subscriptions/UserPageSubscriptionsEditionPreference.tsx (5)
1-11: LGTM! Clean imports and proper client directive.The "use client" directive is correctly placed for this interactive component, and all imports are appropriately scoped.
13-20: LGTM! Props properly typed with readonly modifier.The component signature follows coding guidelines with the readonly modifier on props and clear typing.
30-32: LGTM! Proper state synchronization from props.The effect correctly syncs local state with props changes and has the appropriate dependency array.
34-77: LGTM! Past review comments have been addressed.The error handling and state management issues from the previous review have been resolved:
- ✓
requestAuth()is now inside the try block, ensuring the finally block executes even if authentication fails or throws- ✓ Redundant
setIsUpdatingAllEditions(false)removed from catch block- ✓ Error message extraction improved with fallback chain:
e?.message || String(e) || defaultThe concurrency guard, authentication flow, API call, and state synchronization are all correctly implemented.
27-28: The default eligibility count of 1 is correct and intentional.Since
detailsis explicitly optional (SubscriptionDetails | undefined), andsubscription_eligibility_countis a required field in theSubscriptionDetailsinterface, the patternprops.details?.subscription_eligibility_count ?? 1correctly handles the case when details is undefined (during loading or error states). When data loads, the actual eligibility count from the API response is used. The default of 1 is a safe, reasonable UX fallback and is consistently used across other components in the codebase.
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
components/distribution-plan-tool/create-snapshots/table/CreateSnapshotTableRow.tsx (1)
96-159: Snapshot status cell: avoid initial blank state and fix ARIA on spinnerTwo things to tighten up here:
- The first render can briefly show a blank status when
snapshot.downloaderStatusis unset, becauseisGeneratingSnapshotdefaults tofalseand is only updated inuseEffect. You can derive the initial state fromsnapshot.downloaderStatusto avoid this flicker:- const [isGeneratingSnapshot, setIsGeneratingSnapshot] = - useState<boolean>(false); + const [isGeneratingSnapshot, setIsGeneratingSnapshot] = + useState<boolean>( + !snapshot.downloaderStatus || + ![ + DistributionPlanTokenPoolDownloadStatus.COMPLETED, + DistributionPlanTokenPoolDownloadStatus.FAILED, + ].includes(snapshot.downloaderStatus) + );
- The spinner SVG uses
aria-hidden="true"together withrole="status", which conflicts for assistive tech. Either expose a status or hide the icon. One accessible option is to wrap the SVG and move the status semantics there:- {isGeneratingSnapshot ? ( - <svg - aria-hidden="true" - role="status" - className="tw-w-5 tw-h-5 tw-text-primary-400 tw-animate-spin" - viewBox="0 0 100 101" - fill="none" - xmlns="http://www.w3.org/2000/svg"> + {isGeneratingSnapshot ? ( + <span + role="status" + aria-live="polite" + aria-label="Generating snapshot"> + <svg + aria-hidden="true" + className="tw-w-5 tw-h-5 tw-text-primary-400 tw-animate-spin" + viewBox="0 0 100 101" + fill="none" + xmlns="http://www.w3.org/2000/svg"> ... - </svg> + </svg> + </span>If you want to align even more closely with the guidelines, consider replacing this inline SVG with a FontAwesome spinner icon, but that can be a follow‑up change.
components/user/subscriptions/UserPageSubscriptions.tsx (1)
119-126: Bug:fetchAirdropAddress(function) instead offetchingAirdropAddress(state) in dependency array.Line 121 uses
fetchAirdropAddress(a function) instead offetchingAirdropAddress(the loading state). This is likely a typo and causes the effect to have an incorrect dependency.}, [ fetchingDetails, - fetchAirdropAddress, + fetchingAirdropAddress, fetchingTopUpHistory, fetchingMemeSubscriptions, fetchingSubscriptionLogs, fetchingRedeemedHistory, ]);components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscription.tsx (1)
143-145: Potential crash: accessing.toString()on undefined.If
props.plan.namecontains no numbers,extractAllNumbersreturns an empty array, and[0].toString()throws aTypeError. The nullish coalescing (?? "") won't prevent this.const [tokenId, setTokenId] = useState<string>( - extractAllNumbers(props.plan.name)[0].toString() ?? "" + extractAllNumbers(props.plan.name)[0]?.toString() ?? "" );
♻️ Duplicate comments (2)
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx (1)
37-47: Duplicate function - see earlier comment.This
extractAllNumbersfunction is duplicated. Please extract to a shared utility as suggested in the upload photos component review.components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscription.tsx (1)
130-140: Duplicate function - see earlier comment.
extractAllNumbersis duplicated here as well. Please use the shared utility.
🧹 Nitpick comments (16)
components/distribution-plan-tool/create-snapshots/table/CreateSnapshotTableHeader.tsx (1)
49-51: Empty header cell should have an accessible label for the actions columnThe trailing empty
<th>is likely for row‑level actions, but being completely empty makes it inaccessible to screen readers. Consider adding a visually hidden label instead of leaving it empty, e.g.:- <th - scope="col" - className="tw-px-3 tw-py-3 tw-whitespace-nowrap tw-text-left tw-text-[0.6875rem] tw-leading-[1.125rem] tw-font-medium tw-text-iron-400 tw-uppercase tw-tracking-[0.25px]"></th> + <th + scope="col" + className="tw-px-3 tw-py-3 tw-whitespace-nowrap tw-text-left tw-text-[0.6875rem] tw-leading-[1.125rem] tw-font-medium tw-text-iron-400 tw-uppercase tw-tracking-[0.25px]"> + <span className="tw-sr-only">Actions</span> + </th>This keeps the visual design unchanged but improves accessibility.
components/distribution-plan-tool/common/RoundedManifoldIconButton.tsx (1)
4-18: Disabled wiring looks correct; make props type readonly to match guidelinesThe
disabled={disabled || loading}behavior and visual disabled classes look good and consistent with the other buttons. To align with the “readonly props” guideline for React components, consider updating the inline props type:-}: { - loading: boolean; - onClick: () => void; - disabled?: boolean; -}) { +}: { + readonly loading: boolean; + readonly onClick: () => void; + readonly disabled?: boolean; +}) {components/distribution-plan-tool/common/RoundedCsvIconButton.tsx (1)
4-18: Consistent disabled behavior; also mark props readonlyThe new
disabledprop anddisabled={disabled || loading}usage look correct and match the loading semantics. To be consistent with readonly props guidance, you can tighten the props type:-}: { - loading: boolean; - onClick: () => void; - disabled?: boolean; -}) { +}: { + readonly loading: boolean; + readonly onClick: () => void; + readonly disabled?: boolean; +}) {components/distribution-plan-tool/common/RoundedJsonIconButton.tsx (1)
4-18: Disabled prop integration is good; align props type with readonly patternThe disabled handling (
disabled={disabled || loading}plus disabled styles) is correct and consistent with the other icon buttons. To match the readonly-props convention, you can adjust the props type as follows:-}: { - loading: boolean; - onClick: () => void; - disabled?: boolean; -}) { +}: { + readonly loading: boolean; + readonly onClick: () => void; + readonly disabled?: boolean; +}) {components/user/subscriptions/UserPageSubscriptionsHistory.tsx (1)
85-94: Pagination visibility condition could use page size constant.The condition
props.history.count / 10 > 1uses a hardcoded10. Consider extracting this to align withHISTORY_PAGE_SIZEused in the parent component, or pass it as a prop.- {props.history.count > 0 && props.history.count / 10 > 1 && ( + {props.history.count > 0 && props.history.page_size && props.history.count / props.history.page_size > 1 && (Alternatively, since
Page<T>likely doesn't includepage_size, the simpler fix is:+const PAGE_SIZE = 10; + function RedeemedSubscriptionsAccordion(...) { ... - {props.history.count > 0 && props.history.count / 10 > 1 && ( + {props.history.count > 0 && props.history.count / PAGE_SIZE > 1 && (Also applies to: 122-131, 161-169
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx (2)
165-169: Consider moving interface definitions outside async functions.The
SubscribeBodyandUpdateSubscriptionCountBodyinterfaces are defined inside their respective async functions. Moving them to module scope improves readability and allows reuse.+interface SubscribeBody { + contract: string; + token_id: number; + subscribed: boolean; +} + +interface UpdateSubscriptionCountBody { + contract: string; + token_id: number; + count: number; +} + function SubscriptionRow(...) { ... const submit = async (): Promise<void> => { ... - interface SubscribeBody { - contract: string; - token_id: number; - subscribed: boolean; - } try {Also applies to: 212-216
310-314: Optimistic update followed by async call may cause UI flicker on error.
setSelectedCount(value)is called immediately beforehandleUpdateSubscriptionCount(value), which also sets the value on success. If the API fails, Line 237 reverts tosubscribedCount. This works but briefly shows the new value before reverting on error.Consider making the update purely in the handler or not setting it in
onChange.onChange={(e) => { const value = Number.parseInt(e.target.value, 10); - setSelectedCount(value); handleUpdateSubscriptionCount(value); }}components/user/subscriptions/UserPageSubscriptionsTopUp.tsx (2)
368-373: Avoid usingquerySelectorwithsetTimeoutfor focus management.Using
document.querySelector('input[placeholder="count"]')is brittle and could break if another input with the same placeholder exists. Consider using a React ref instead.+import { useRef } from "react"; + export default function UserPageSubscriptionsTopUp() { + const otherInputRef = useRef<HTMLInputElement>(null); ... onClick={() => { setSelectedOption("other"); setError(""); - setTimeout(() => { - const input = document.querySelector( - 'input[placeholder="count"]' - ) as HTMLInputElement; - input?.focus(); - }, 0); + setTimeout(() => otherInputRef.current?.focus(), 0); }}> ... <Form.Control + ref={otherInputRef} type="number" min={1} placeholder="count"Also applies to: 383-388
416-428: Redundant try/catch branches—all paths execute the same code.The try/catch/else branches all set
memeCount(value)andsetSelectedOption("other"). This can be simplified.onChange={(e) => { setError(""); const value = e.target.value; - try { - const parsed = parseInt(value); - if (!isNaN(parsed) && parsed > 0) { - setMemeCount(value); - setSelectedOption("other"); - } else { - setMemeCount(value); - setSelectedOption("other"); - } - } catch { - setMemeCount(value); - setSelectedOption("other"); - } + setMemeCount(value); + setSelectedOption("other"); }}The validation is already handled in
handleSend(), so there's no need for additional parsing logic here.hooks/useManifoldClaim.ts (1)
210-221: Consider handling errors before initial claim data loads.The error handler only sets
isError: trueifprevClaimexists. IfreadContract.erroroccurs before any successful data fetch (i.e.,prevClaimis undefined), the error state won't be stored. Depending on your UX intent, you may want to initialize a partial claim withisError: trueso the UI can display an error message instead of an indefinite loading state.Example approach:
useEffect(() => { if (readContract.error) { setClaim((prevClaim) => { if (prevClaim) { return { ...prevClaim, isError: true, }; + } else { + return { + instanceId: 0, + total: 0, + totalMax: 0, + remaining: 0, + cost: 0, + startDate: 0, + endDate: 0, + status: ManifoldClaimStatus.UPCOMING, + phase: ManifoldPhase.PUBLIC, + isFetching: false, + isFinalized: false, + isSoldOut: false, + isError: true, + }; } }); onError?.(); } }, [readContract.error, onError]);components/mint-countdown-box/MemePageMintCountdown.tsx (1)
20-22: Memoize the error callback to avoid unnecessary effect executions.The inline arrow function is recreated on every render, causing the
useEffectinuseManifoldClaim(which depends ononError) to re-run unnecessarily.Apply this diff:
+import { useCallback, useEffect, useState } from "react"; + export default function MemePageMintCountdown( props: Readonly<{ nft_id: number; hide_mint_btn?: boolean; }> ) { const [isError, setIsError] = useState(false); - const manifoldClaim = useMemesManifoldClaim(props.nft_id, () => { - setIsError(true); - }); + const handleError = useCallback(() => { + setIsError(true); + }, []); + const manifoldClaim = useMemesManifoldClaim(props.nft_id, handleError);components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTable.tsx (1)
91-103: Remove redundant type assertion.
null as nullis unnecessary sincenullalready has thenulltype:const publicRow: ReviewDistributionPlanTablePhase = { phase: { id: PUBLIC_SUBSCRIPTIONS_PHASE_ID, phaseId: PUBLIC_SUBSCRIPTIONS_PHASE_ID, - componentId: null as null, + componentId: null, type: ReviewDistributionPlanTableItemType.PHASE, name: "Public", description: "Auto-generated", walletsCount: 0, spotsCount: 0, }, components: [], };components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsx (1)
9-19: Duplicate utility function across files.
extractAllNumbersis duplicated in three files: this file,ReviewDistributionPlanTableSubscriptionFooter.tsx, andReviewDistributionPlanTableSubscription.tsx. Extract this to a shared utility module.Create a shared utility, e.g., in
helpers/Helpers.ts:export function extractAllNumbers(str: string): number[] { const regex = /\d+/g; const numbers = []; let match; while ((match = regex.exec(str)) !== null) { numbers.push(parseInt(match[0])); } return numbers; }components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx (2)
92-99: Extract duplicate overview refetch logic.The same fetch-and-set pattern for
DistributionOverviewappears after reset (line 92-99), upload (line 210-213), and finalize (line 272-275).Extract to a reusable function:
const refetchOverview = async (contract: string, tokenId: string) => { try { const data = await commonApiFetch<DistributionOverview>({ endpoint: `distributions/${contract}/${tokenId}/overview`, }); setOverview(data); } catch (error) { console.error("Failed to fetch distribution overview:", error); } };Also applies to: 210-213, 272-275
183-232: Consider extracting inline handler.The
onUploadhandler is ~50 lines. Extracting to a named function would improve readability:const handlePhotoUpload = async (contract: string, tokenId: string, files: File[]) => { setShowUploadPhotos(false); setIsUploading(true); // ... rest of logic };components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscription.tsx (1)
51-82: Consolidate duplicate button blocks.The public and non-public buttons are nearly identical, differing only in the label text. Consider consolidating:
- {!isPublic && ( - <button - onClick={() => setShowConfirm(true)} - disabled={downloading} - type="button" - className="tw-px-3 tw-group tw-rounded-full tw-group tw-flex tw-items-center tw-justify-center tw-h-8 tw-text-xs tw-font-medium tw-border-none tw-ring-1 tw-ring-inset tw-text-white tw-bg-blue-500 tw-ring-iron-500/50 hover:tw-bg-blue-500/50 tw-ease-out tw-transition tw-duration-300"> - {downloading ? ( - <span className="d-flex gap-2 align-items-center"> - <CircleLoader /> - <span>Downloading</span> - </span> - ) : ( - <>Subscription Lists</> - )} - </button> - )} - {isPublic && ( - <button - onClick={() => setShowConfirm(true)} - disabled={downloading} - type="button" - className="tw-px-3 tw-group tw-rounded-full tw-group tw-flex tw-items-center tw-justify-center tw-h-8 tw-text-xs tw-font-medium tw-border-none tw-ring-1 tw-ring-inset tw-text-white tw-bg-blue-500 tw-ring-iron-500/50 hover:tw-bg-blue-500/50 tw-ease-out tw-transition tw-duration-300"> - {downloading ? ( - <span className="d-flex gap-2 align-items-center"> - <CircleLoader /> - <span>Downloading</span> - </span> - ) : ( - <>Public Subscriptions</> - )} - </button> - )} + <button + onClick={() => setShowConfirm(true)} + disabled={downloading} + type="button" + className="tw-px-3 tw-group tw-rounded-full tw-group tw-flex tw-items-center tw-justify-center tw-h-8 tw-text-xs tw-font-medium tw-border-none tw-ring-1 tw-ring-inset tw-text-white tw-bg-blue-500 tw-ring-iron-500/50 hover:tw-bg-blue-500/50 tw-ease-out tw-transition tw-duration-300"> + {downloading ? ( + <span className="d-flex gap-2 align-items-center"> + <CircleLoader /> + <span>Downloading</span> + </span> + ) : ( + <>{isPublic ? "Public Subscriptions" : "Subscription Lists"}</> + )} + </button>
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (22)
components/distribution-plan-tool/common/RoundedCsvIconButton.tsx(1 hunks)components/distribution-plan-tool/common/RoundedJsonIconButton.tsx(1 hunks)components/distribution-plan-tool/common/RoundedManifoldIconButton.tsx(1 hunks)components/distribution-plan-tool/create-snapshots/table/CreateSnapshotTableHeader.tsx(1 hunks)components/distribution-plan-tool/create-snapshots/table/CreateSnapshotTableRow.tsx(2 hunks)components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTable.tsx(2 hunks)components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableRow.tsx(2 hunks)components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscription.tsx(6 hunks)components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx(4 hunks)components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsx(1 hunks)components/distribution-plan-tool/review-distribution-plan/table/constants.ts(1 hunks)components/distribution/Distribution.tsx(9 hunks)components/manifoldMinting/ManifoldMinting.tsx(2 hunks)components/mint-countdown-box/MemePageMintCountdown.tsx(3 hunks)components/mint-countdown-box/MintCountdownBox.tsx(2 hunks)components/user/subscriptions/UserPageSubscriptions.module.scss(2 hunks)components/user/subscriptions/UserPageSubscriptions.tsx(4 hunks)components/user/subscriptions/UserPageSubscriptionsHistory.tsx(4 hunks)components/user/subscriptions/UserPageSubscriptionsTopUp.tsx(6 hunks)components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx(8 hunks)entities/IDistribution.ts(1 hunks)hooks/useManifoldClaim.ts(2 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx,js,jsx}: Do not include any comments in the code; it should be self-explanatory
Write correct, up-to-date, bug-free, fully componentized, secure, and efficient code
Include all required imports and ensure proper naming of key components
Use NextJS features that match the current version
**/*.{ts,tsx,js,jsx}: Enforce ≥ 80% line coverage for files changed sincemainvianpm run test
All code must pass ESLint (Next's Core Web Vitals + React Hooks rules) vianpm run lint
Use<Link>from Next.js for internal navigation instead of<a>tags or HTML anchors
Use<Image>fromnext/imageinstead of HTML<img>elements
Files:
components/mint-countdown-box/MemePageMintCountdown.tsxcomponents/distribution-plan-tool/review-distribution-plan/table/constants.tscomponents/distribution-plan-tool/common/RoundedCsvIconButton.tsxhooks/useManifoldClaim.tscomponents/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableRow.tsxcomponents/manifoldMinting/ManifoldMinting.tsxcomponents/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTable.tsxcomponents/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsxcomponents/user/subscriptions/UserPageSubscriptionsTopUp.tsxcomponents/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsxcomponents/mint-countdown-box/MintCountdownBox.tsxcomponents/distribution-plan-tool/create-snapshots/table/CreateSnapshotTableRow.tsxcomponents/user/subscriptions/UserPageSubscriptionsUpcoming.tsxcomponents/distribution-plan-tool/common/RoundedJsonIconButton.tsxentities/IDistribution.tscomponents/distribution-plan-tool/common/RoundedManifoldIconButton.tsxcomponents/distribution-plan-tool/create-snapshots/table/CreateSnapshotTableHeader.tsxcomponents/user/subscriptions/UserPageSubscriptions.tsxcomponents/user/subscriptions/UserPageSubscriptionsHistory.tsxcomponents/distribution/Distribution.tsxcomponents/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscription.tsx
**/*.{tsx,jsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{tsx,jsx}: Use FontAwesome for icons in React components
Use TailwindCSS for styling in React components
Use react-query for data fetching
Always addreadonlybefore props in React components
Files:
components/mint-countdown-box/MemePageMintCountdown.tsxcomponents/distribution-plan-tool/common/RoundedCsvIconButton.tsxcomponents/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableRow.tsxcomponents/manifoldMinting/ManifoldMinting.tsxcomponents/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTable.tsxcomponents/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsxcomponents/user/subscriptions/UserPageSubscriptionsTopUp.tsxcomponents/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsxcomponents/mint-countdown-box/MintCountdownBox.tsxcomponents/distribution-plan-tool/create-snapshots/table/CreateSnapshotTableRow.tsxcomponents/user/subscriptions/UserPageSubscriptionsUpcoming.tsxcomponents/distribution-plan-tool/common/RoundedJsonIconButton.tsxcomponents/distribution-plan-tool/common/RoundedManifoldIconButton.tsxcomponents/distribution-plan-tool/create-snapshots/table/CreateSnapshotTableHeader.tsxcomponents/user/subscriptions/UserPageSubscriptions.tsxcomponents/user/subscriptions/UserPageSubscriptionsHistory.tsxcomponents/distribution/Distribution.tsxcomponents/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscription.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: All code must pass TypeScript type checking vianpm run type-check(tsc --noEmit)
Use'use cache'directive at the top of Server Components or functions to explicitly opt-in to caching in Next.js 16
Files:
components/mint-countdown-box/MemePageMintCountdown.tsxcomponents/distribution-plan-tool/review-distribution-plan/table/constants.tscomponents/distribution-plan-tool/common/RoundedCsvIconButton.tsxhooks/useManifoldClaim.tscomponents/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableRow.tsxcomponents/manifoldMinting/ManifoldMinting.tsxcomponents/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTable.tsxcomponents/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsxcomponents/user/subscriptions/UserPageSubscriptionsTopUp.tsxcomponents/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsxcomponents/mint-countdown-box/MintCountdownBox.tsxcomponents/distribution-plan-tool/create-snapshots/table/CreateSnapshotTableRow.tsxcomponents/user/subscriptions/UserPageSubscriptionsUpcoming.tsxcomponents/distribution-plan-tool/common/RoundedJsonIconButton.tsxentities/IDistribution.tscomponents/distribution-plan-tool/common/RoundedManifoldIconButton.tsxcomponents/distribution-plan-tool/create-snapshots/table/CreateSnapshotTableHeader.tsxcomponents/user/subscriptions/UserPageSubscriptions.tsxcomponents/user/subscriptions/UserPageSubscriptionsHistory.tsxcomponents/distribution/Distribution.tsxcomponents/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscription.tsx
🧠 Learnings (3)
📚 Learning: 2025-11-25T08:37:14.939Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T08:37:14.939Z
Learning: Use TypeScript and React functional components with hooks
Applied to files:
components/mint-countdown-box/MemePageMintCountdown.tsxcomponents/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTable.tsxcomponents/user/subscriptions/UserPageSubscriptionsTopUp.tsx
📚 Learning: 2025-11-25T08:35:58.721Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T08:35:58.721Z
Learning: Applies to **/*.{tsx,jsx} : Use FontAwesome for icons in React components
Applied to files:
components/user/subscriptions/UserPageSubscriptionsTopUp.tsx
📚 Learning: 2025-11-25T08:37:14.939Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T08:37:14.939Z
Learning: Fix issues with modernization aligned to React 19.2, React Compiler, and Next.js 16 conventions; do not add `// eslint-disable` comments unless explicitly instructed
Applied to files:
components/distribution/Distribution.tsx
🧬 Code graph analysis (7)
components/mint-countdown-box/MemePageMintCountdown.tsx (1)
hooks/useManifoldClaim.ts (1)
useMemesManifoldClaim(237-245)
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableRow.tsx (5)
components/distribution-plan-tool/review-distribution-plan/table/constants.ts (1)
PUBLIC_SUBSCRIPTIONS_PHASE_ID(1-1)components/distribution-plan-tool/common/DistributionPlanTableRowWrapper.tsx (1)
DistributionPlanTableRowWrapper(3-17)components/distribution-plan-tool/common/RoundedJsonIconButton.tsx (1)
RoundedJsonIconButton(4-24)components/distribution-plan-tool/common/RoundedCsvIconButton.tsx (1)
RoundedCsvIconButton(4-24)components/distribution-plan-tool/common/RoundedManifoldIconButton.tsx (1)
RoundedManifoldIconButton(4-24)
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTable.tsx (3)
components/auth/Auth.tsx (1)
AuthContext(83-93)components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscription.tsx (1)
isSubscriptionsAdmin(218-224)components/distribution-plan-tool/review-distribution-plan/table/constants.ts (1)
PUBLIC_SUBSCRIPTIONS_PHASE_ID(1-1)
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsx (3)
components/allowlist-tool/allowlist-tool.types.ts (1)
AllowlistDescription(12-18)constants.ts (1)
MEMES_CONTRACT(5-5)helpers/Helpers.ts (1)
formatAddress(31-46)
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx (3)
components/show-more-button/ShowMoreButton.tsx (1)
ShowMoreButton(7-35)services/api/common-api.ts (1)
commonApiPost(230-249)components/meme-calendar/meme-calendar.helpers.tsx (1)
formatFullDate(669-677)
components/distribution/Distribution.tsx (5)
entities/IDistribution.ts (1)
Distribution(8-21)helpers/Helpers.ts (2)
numberWithCommas(83-100)areEqualAddresses(149-154)constants.ts (1)
MEMES_CONTRACT(5-5)components/the-memes/UpcomingMemePage.tsx (1)
UpcomingMemePage(4-14)components/not-found/NotFound.tsx (1)
NotFound(7-51)
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscription.tsx (2)
components/distribution-plan-tool/review-distribution-plan/table/constants.ts (1)
PUBLIC_SUBSCRIPTIONS_PHASE_ID(1-1)components/distribution-plan-tool/common/CircleLoader.tsx (1)
CircleLoader(9-39)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (32)
components/distribution-plan-tool/create-snapshots/table/CreateSnapshotTableHeader.tsx (1)
6-24: New Status / Contract number headers look consistent; verify body columns are updatedThe added “Status” and “Contract number” headers match the existing typography and spacing, so structurally this looks good. Please double‑check that the corresponding
<td>cells in the snapshot rows were updated to include these new columns in the same order to avoid misaligned data and header/body mismatch.components/distribution-plan-tool/create-snapshots/table/CreateSnapshotTableRow.tsx (1)
221-225: Tooltip style adjustment looks goodThe Tooltip
styleobject and JSX closure are syntactically correct and keep the existing behavior; no changes needed here.components/manifoldMinting/ManifoldMinting.tsx (2)
148-148: LGTM: Clean formatting change.The heading formatting is clear and follows standard JSX conventions.
514-520: Type safety verified —airdropsproperty exists and is properly typed.The
airdropsproperty is defined asnumberin theDistributioninterface (entities/IDistribution.ts:15), confirming type safety. The optional chainingdistribution?.airdropscorrectly handles undefined distribution instances. The conditional rendering pattern is sound: the UI displays only whenairdrops > 0, which is intentional.components/user/subscriptions/UserPageSubscriptions.module.scss (3)
27-51: Table styling with alternating rows looks good.The alternating row backgrounds (#242428 for even, #1c1c21 for odd) provide good visual distinction. The
!importantusage is acceptable here for overriding Bootstrap defaults.
83-110: Button styling is well-structured with proper state handling.Good use of
:not(:disabled)selectors for hover/active states, and the disabled state properly conveys the non-interactive state with reduced opacity andcursor: not-allowed.
112-122: Radio button styling uses global selectors correctly.The
:global()wrapper is necessary here since Bootstrap's Form.Check generates class names that would otherwise be scoped.components/user/subscriptions/UserPageSubscriptionsHistory.tsx (2)
21-59: Component structure with readonly props follows guidelines.The main component properly destructures paginated data and provides setters for page navigation. The structure is clean with proper separation of concerns.
240-280: RedeemedEntry handles optional transaction_date gracefully.The fallback to
created_atwhentransaction_dateis undefined ensures dates are always displayed.components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx (3)
125-134: State synchronization pattern is correct.The
useMemoforsubscribedCountanduseEffectto syncselectedCountproperly handles external updates to the subscription data.
200-246: handleUpdateSubscriptionCount properly handles errors and resets state.Good error handling with state reversion on failure. The
finallyblock ensuresisSubmittingis always reset.
298-330: Quantity selector UI implementation is well-structured.The dropdown is properly disabled based on
eligibilityCount,readonly,isSubmitting, andminting_todaystates. The conditional rendering when subscribed is correct.components/user/subscriptions/UserPageSubscriptionsTopUp.tsx (4)
142-151: Body scroll lock with proper cleanup.The effect correctly locks scroll when modal is shown and cleans up on unmount. Good pattern for modal overlays.
153-176: Escape key handler with proper dependency management.The
closeModalcallback is properly memoized withuseCallback, and the escape handler is correctly added/removed based on modal state.
491-543: Modal portal implementation is correct.The
mountedcheck beforecreatePortalprevents SSR hydration mismatches. The AnimatePresence wrapper enables smooth enter/exit animations.
548-584: CardCountOption component is clean and reusable.The component properly uses readonly props and handles selection state. Good separation of concerns.
components/user/subscriptions/UserPageSubscriptions.tsx (3)
263-267: Good pattern: Reusable column configuration object.The
colConfigobject provides a single source of truth for consistent grid layout across all subscription panels. This reduces duplication and makes layout changes easier.
287-318: Grid layout with new EditionPreference component is well-structured.The four columns (Balance, AirdropAddress, Mode, EditionPreference) use consistent sizing via
colConfig. The responsive breakpoints (xs=12, md=6) provide good mobile/desktop layouts.
322-328: Conditional TopUp rendering for connected accounts is correct.The TopUp section is appropriately gated behind
isConnectedAccountcheck, preventing unauthorized users from seeing top-up controls.components/distribution-plan-tool/review-distribution-plan/table/constants.ts (1)
1-1: LGTM!Centralizing this magic string as a constant is the right approach for maintainability and consistency across the table components.
entities/IDistribution.ts (1)
4-5: Verify API contract for new fields.These fields are typed as required (
number), but inDistribution.tsx(lines 157-158), the code uses optional chaining (p?.spots_airdrop ?? 0), suggesting they might be optional. If the API can returnnullor omit these fields, consider making them optional:interface DistributionPhaseEntry { phase: string; spots: number; - spots_airdrop: number; - spots_allowlist: number; + spots_airdrop?: number; + spots_allowlist?: number; }components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableRow.tsx (1)
192-214: LGTM!The
isPublicflag correctly guards the display of counts (showing "-" instead of 0) and disables action buttons to prevent invalid API calls for the synthetic public phase row. This aligns well with the button components that already support thedisabledprop.components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTable.tsx (1)
50-108: Well-structured refactor of rows construction.The use of
reduceto accumulate both components andspotsCountin a single pass is efficient. The conditional addition of the public phase row for admins is cleanly implemented with proper dependency tracking. Based on learnings, this follows React functional components with hooks pattern correctly.components/distribution/Distribution.tsx (2)
43-44: LGTM!Deriving
nftNanandnftIddirectly from params is cleaner than the previous useEffect synchronization approach. This reduces state management complexity and potential stale state issues.
309-311: Good early return for invalid route.Returning
NotFoundearly when the route param is not a valid number is the correct approach—it prevents unnecessary API calls and provides immediate user feedback.components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsx (3)
30-32: Token ID won't update ifplanprop changes.
useStateinitializer runs only once on mount. If this modal is reused for different plans without unmounting,tokenIdwill show stale data from the first plan.Verify the modal is unmounted between uses, or add a
useEffectto sync state:useEffect(() => { setTokenId(extractAllNumbers(props.plan.name)[0]?.toString() ?? ""); }, [props.plan.name]);
48-68: LGTM!File validation logic correctly handles empty file lists, invalid MIME types, and size limits with clear error messages.
121-228: LGTM!Modal structure is well-organized with proper state reset on close, disabled button guards, and clear user feedback for validation errors and selected files.
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx (2)
49-73: LGTM with minor observation.The overview fetch logic is well-structured with proper guards. Note that
connectedProfileobject reference changes could trigger refetches, but the admin check guards against unnecessary API calls.
114-175: LGTM!Button layout with loading states, disabled guards, and status indicators (photo count, normalization checkmark) provides good user feedback.
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscription.tsx (2)
179-188: LGTM!The normalization warning provides clear feedback to admins about potential data overwrite, with appropriate styling.
206-293: LGTM!CSV download utilities (
mergeResults,convertToCSV,downloadCSV,processResults) are well-structured and handle the different output types correctly for public vs non-public phases.
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/subscriptions/UserPageSubscriptionsTopUp.tsx (1)
257-265: Use a standard anchor tag for external navigation with target="_blank".Using Next.js
Linkwithwindow.location.hrefas the href is an anti-pattern. TheLinkcomponent is designed for client-side routing within the app, not for opening the current URL in a new tab.Apply this diff to use a standard anchor tag instead:
- <Link - href={window.location.href} - className="text-center pt-2 pb-2" - target="_blank" - rel="noopener noreferrer"> + <a + href={typeof window !== 'undefined' ? window.location.href : ''} + className="text-center pt-2 pb-2" + target="_blank" + rel="noopener noreferrer"> <button className="btn btn-light" style={{ width: "100%" }}> Top-up on 6529.io </button> - </Link> + </a>
♻️ Duplicate comments (3)
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsx (1)
146-146: Past review comment still applies:parseInton empty string returnsNaN, causing input display issues.When the user clears the input field,
tokenIdbecomes"", andparseInt("")returnsNaN, which breaks the controlled input display.Apply this diff to fix the issue:
- value={Number.parseInt(tokenId, 10)} + value={tokenId === "" ? "" : Number.parseInt(tokenId, 10)}Alternatively, store
tokenIdas a number or use a more robust approach:- value={Number.parseInt(tokenId, 10)} + value={tokenId}Then ensure validation elsewhere handles non-numeric strings appropriately (which lines 100-101 already do).
components/user/subscriptions/UserPageSubscriptionsTopUp.tsx (1)
507-508: Missing space between card count and "Cards".This issue was previously identified. There's no space between the card count and the word "Cards", resulting in display like "5Cards" instead of "5 Cards".
Apply this diff to fix:
<p className="tw-text-iron-400 tw-text-sm tw-mt-1"> - {(topUpAmount / MEMES_MINT_PRICE).toLocaleString()} - Cards -{" "} + {(topUpAmount / MEMES_MINT_PRICE).toLocaleString()}{" "} + Cards -{" "} {numberWithCommasFromString( topUpAmount.toString() )}{" "}components/distribution/Distribution.tsx (1)
150-172: NormalizegetSpotsForPhasereturn type (string/JSX/undefined).This helper currently returns
"", a JSX element, orundefined(implicit fall-through whencount <= 0), which complicates typing and makes the calling<td>harder to reason about. Aligning onJSX.Element | null(orReact.ReactNode) would be cleaner and match React conventions. This re-iterates the earlier feedback on this function.You can make the return type explicit and use
nullfor “no content” paths:- function getSpotsForPhase(d: Distribution, phase: string) { - if (phase.toUpperCase() === "AIRDROP") { - return ""; - } - const p = d.allowlist.find((a) => a.phase === phase); - const count = p?.spots ?? 0; - if (count > 0) { - const spotsAirdrop = p?.spots_airdrop ?? 0; - const spotsAllowlist = p?.spots_allowlist ?? 0; - - if (!spotsAirdrop && !spotsAllowlist) { - return ""; - } - - return ( - <span className="tw-text-iron-400 tw-text-sm"> - {spotsAirdrop > 0 ? numberWithCommas(spotsAirdrop) : "0"} - {" | "} - {spotsAllowlist > 0 ? numberWithCommas(spotsAllowlist) : "0"} - </span> - ); - } - } + function getSpotsForPhase( + d: Distribution, + phase: string + ): JSX.Element | null { + if (phase.toUpperCase() === "AIRDROP") { + return null; + } + + const p = d.allowlist.find((a) => a.phase === phase); + const count = p?.spots ?? 0; + if (!p || count <= 0) { + return null; + } + + const spotsAirdrop = p.spots_airdrop ?? 0; + const spotsAllowlist = p.spots_allowlist ?? 0; + + if (!spotsAirdrop && !spotsAllowlist) { + return null; + } + + return ( + <span className="tw-text-iron-400 tw-text-sm"> + {spotsAirdrop > 0 ? numberWithCommas(spotsAirdrop) : "0"} + {" | "} + {spotsAllowlist > 0 ? numberWithCommas(spotsAllowlist) : "0"} + </span> + ); + }This keeps the caller logic unchanged while making the function’s contract much clearer.
🧹 Nitpick comments (6)
components/user/subscriptions/UserPageSubscriptionsTopUp.tsx (1)
88-96: Consider extracting optionCountMap outside the function.The
optionCountMapis recreated on everyhandleSendinvocation. For better performance, consider defining it outside the function or usinguseMemoif it depends on reactive values.Apply this diff to extract the constant:
+const OPTION_COUNT_MAP_KEYS = { + ONE: "1", + SZN: "szn", + YEAR: "year", + EPOCH: "epoch", + PERIOD: "period", + ERA: "era", + EON: "eon", +} as const; + export default function UserPageSubscriptionsTopUp() { const { isIos } = useCapacitor(); // ... other state ... function handleSend() { setError(""); let count: number; if (selectedOption && selectedOption !== "other") { - const optionCountMap: Record<string, number> = { - "1": 1, - szn: remainingMintsForSeason, - year: remainingMintsForYear, - epoch: remainingMintsForEpoch, - period: remainingMintsForPeriod, - era: remainingMintsForEra, - eon: remainingMintsForEon, - }; + const optionCountMap: Record<string, number> = { + [OPTION_COUNT_MAP_KEYS.ONE]: 1, + [OPTION_COUNT_MAP_KEYS.SZN]: remainingMintsForSeason, + [OPTION_COUNT_MAP_KEYS.YEAR]: remainingMintsForYear, + [OPTION_COUNT_MAP_KEYS.EPOCH]: remainingMintsForEpoch, + [OPTION_COUNT_MAP_KEYS.PERIOD]: remainingMintsForPeriod, + [OPTION_COUNT_MAP_KEYS.ERA]: remainingMintsForEra, + [OPTION_COUNT_MAP_KEYS.EON]: remainingMintsForEon, + }; count = optionCountMap[selectedOption];components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscription.tsx (3)
40-115: Admin gating + public/non‑public buttons look consistent; consider deduplicating button markupThe admin/PHASE guard and
isPublicflag derived fromPUBLIC_SUBSCRIPTIONS_PHASE_IDare coherent and correctly prevent non‑admins or non‑PHASE rows from seeing download actions. The two button branches, however, duplicate the same structure with only the label text differing; extracting a small helper or passing the label as a variable would reduce repetition and keep future styling tweaks in one place.
164-177: KeeptokenIdas a string in the input value to avoidNaNedge casesYou currently store
tokenIdas a string but render the input withvalue={Number.parseInt(tokenId, 10)}. WhentokenIdis empty or invalid this can produceNaN, which React doesn’t handle as cleanly for controlled inputs as a plain string.Since you already validate with
Number.parseIntin the button’sdisabledcondition, you can keep the input purely string‑based:- Token ID:{" "} - <input - style={{ - color: "black", - width: "100px", - }} - min={1} - type="number" - value={Number.parseInt(tokenId, 10)} - onChange={(e) => { - setTokenId(e.target.value); - }} - /> + Token ID:{" "} + <input + style={{ + color: "black", + width: "100px", + }} + min={1} + type="number" + value={tokenId} + onChange={(e) => { + setTokenId(e.target.value); + }} + /> @@ - <Button - disabled={!tokenId || Number.isNaN(Number.parseInt(tokenId, 10))} + <Button + disabled={!tokenId || Number.isNaN(Number.parseInt(tokenId, 10))} variant="primary" onClick={() => props.onConfirm(contract, tokenId)}>This keeps the controlled input simple while preserving your current validation.
Also applies to: 196-200
226-249: Align public handling inprocessResultswithPUBLIC_SUBSCRIPTIONS_PHASE_ID
downloaddistinguishes phases viaphaseId, butprocessResultsbranches onphaseName === "public". Now that you introducedPUBLIC_SUBSCRIPTIONS_PHASE_IDand are using it elsewhere (e.g.,isPublicinSubscriptionLinks), relying on the literal"public"here is a bit brittle and can drift if display names change.Consider threading
phaseId(or a booleanisPublic) intoprocessResultsso that the CSV behavior is driven by the same source of truth as the UI:-export const download = async ( - contract: string, - tokenId: string, - planId: string, - phaseId: string, - phaseName: string -) => { +export const download = async ( + contract: string, + tokenId: string, + planId: string, + phaseId: string, + phaseName: string +) => { @@ - .then((response) => { - processResults(phaseName, response); + .then((response) => { + processResults(phaseId, phaseName, response); @@ -const processResults = (phaseName: string, results: SubscriptionResult) => { - if (phaseName === "public") { +const processResults = ( + phaseId: string, + phaseName: string, + results: SubscriptionResult +) => { + if (phaseId === PUBLIC_SUBSCRIPTIONS_PHASE_ID) {This keeps “publicness” keyed off the same constant instead of a magic string while preserving filenames via
phaseName.Also applies to: 251-264
components/distribution/Distribution.tsx (2)
43-44: TightennftIdvalidation and re-check hooks behavior.
Number.parseIntwill treat values like"123abc"as valid IDs. If the route param is intended to be strictly numeric, consider validating the raw string instead of relying onparseInt:- const isValidNftId = !Number.isNaN(Number.parseInt(params?.id as string, 10)); - const nftId = isValidNftId ? (params?.id as string) : ""; + const rawId = params?.id as string | undefined; + const isValidNftId = typeof rawId === "string" && /^\d+$/.test(rawId); + const nftId = isValidNftId ? rawId : "";Also, since
nftIdnow gates all fetching logic, it’s worth re-running the React Hooks lint to ensure the effects that depend onnftId/pagePropsdon’t get flagged for missing dependencies; a small tweak like using a functionalsetPagePropsupdate in the search reset effect can help keep dependencies minimal without adding// eslint-disablecomments. Based on learnings, prefer adjusting dependencies over disabling ESLint.
237-241: Consider moving flex layout into an inner wrapper instead of<td>.Using
tw-flexdirectly on<td>changes the cell’s display toflex, which can sometimes interact oddly with table layouts and responsive behavior. Consider keeping the<td>using the table display model and wrapping the contents in a flex container:- <td - key={`${p}-${d.wallet}`} - className="tw-flex tw-justify-center tw-items-center"> - {getCountForPhase(d, p)} - {getSpotsForPhase(d, p)} - </td> + <td key={`${p}-${d.wallet}`}> + <div className="tw-flex tw-justify-center tw-items-center"> + {getCountForPhase(d, p)} + {getSpotsForPhase(d, p)} + </div> + </td>Not mandatory, but it keeps table semantics a bit cleaner.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscription.tsx(6 hunks)components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx(4 hunks)components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsx(1 hunks)components/distribution/Distribution.tsx(9 hunks)components/user/subscriptions/UserPageSubscriptions.module.scss(2 hunks)components/user/subscriptions/UserPageSubscriptionsTopUp.tsx(6 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- components/user/subscriptions/UserPageSubscriptions.module.scss
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx,js,jsx}: Do not include any comments in the code; it should be self-explanatory
Write correct, up-to-date, bug-free, fully componentized, secure, and efficient code
Include all required imports and ensure proper naming of key components
Use NextJS features that match the current version
**/*.{ts,tsx,js,jsx}: Enforce ≥ 80% line coverage for files changed sincemainvianpm run test
All code must pass ESLint (Next's Core Web Vitals + React Hooks rules) vianpm run lint
Use<Link>from Next.js for internal navigation instead of<a>tags or HTML anchors
Use<Image>fromnext/imageinstead of HTML<img>elements
Files:
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsxcomponents/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsxcomponents/user/subscriptions/UserPageSubscriptionsTopUp.tsxcomponents/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscription.tsxcomponents/distribution/Distribution.tsx
**/*.{tsx,jsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{tsx,jsx}: Use FontAwesome for icons in React components
Use TailwindCSS for styling in React components
Use react-query for data fetching
Always addreadonlybefore props in React components
Files:
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsxcomponents/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsxcomponents/user/subscriptions/UserPageSubscriptionsTopUp.tsxcomponents/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscription.tsxcomponents/distribution/Distribution.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: All code must pass TypeScript type checking vianpm run type-check(tsc --noEmit)
Use'use cache'directive at the top of Server Components or functions to explicitly opt-in to caching in Next.js 16
Files:
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsxcomponents/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsxcomponents/user/subscriptions/UserPageSubscriptionsTopUp.tsxcomponents/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscription.tsxcomponents/distribution/Distribution.tsx
🧠 Learnings (3)
📚 Learning: 2025-11-25T08:35:58.721Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T08:35:58.721Z
Learning: Applies to **/*.{tsx,jsx} : Use FontAwesome for icons in React components
Applied to files:
components/user/subscriptions/UserPageSubscriptionsTopUp.tsx
📚 Learning: 2025-11-25T08:37:14.939Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T08:37:14.939Z
Learning: Use TypeScript and React functional components with hooks
Applied to files:
components/user/subscriptions/UserPageSubscriptionsTopUp.tsx
📚 Learning: 2025-11-25T08:37:14.939Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T08:37:14.939Z
Learning: Fix issues with modernization aligned to React 19.2, React Compiler, and Next.js 16 conventions; do not add `// eslint-disable` comments unless explicitly instructed
Applied to files:
components/distribution/Distribution.tsx
🧬 Code graph analysis (4)
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx (3)
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscription.tsx (1)
isSubscriptionsAdmin(218-224)constants.ts (1)
MEMES_CONTRACT(5-5)components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsx (1)
UploadDistributionPhotosModal(21-231)
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsx (3)
components/allowlist-tool/allowlist-tool.types.ts (1)
AllowlistDescription(12-18)constants.ts (1)
MEMES_CONTRACT(5-5)helpers/Helpers.ts (1)
formatAddress(31-46)
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscription.tsx (1)
components/distribution-plan-tool/review-distribution-plan/table/constants.ts (1)
PUBLIC_SUBSCRIPTIONS_PHASE_ID(1-1)
components/distribution/Distribution.tsx (4)
entities/IDistribution.ts (1)
Distribution(8-21)helpers/Helpers.ts (2)
numberWithCommas(83-100)areEqualAddresses(149-154)constants.ts (1)
MEMES_CONTRACT(5-5)components/the-memes/UpcomingMemePage.tsx (1)
UpcomingMemePage(4-14)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (12)
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx (4)
49-73: LGTM: Distribution overview fetch is properly implemented.The useEffect correctly fetches the distribution overview on component mount and when dependencies change. Error handling is appropriate, and the early returns prevent unnecessary API calls.
115-175: LGTM: UI layout properly reflects the new workflow.The vertical stacking with three distinct button groups (Reset, Upload, Finalize) clearly separates the actions. The photo count indicator and normalization status indicator provide helpful visual feedback to admins.
249-294: <!-- [scratchpad_start]
[task review_verification 1/1]
[observations]
- This is a frontend repository (6529seize-frontend)
- Backend API endpoint is configured via publicEnv.API_ENDPOINT
- commonApiPost builds URLs as /api/${endpoint}
- normalize endpoint will be:
/api/distributions/${contract}/${tokenId}/normalize- The backend API is external and not included in this frontend codebase
- Cannot verify backend implementation from frontend codebase
[analysis]
Frontend code verification:
- Response type is correctly specified: { success: boolean; message?: string; error?: string }
- Response is parsed via JSON.parse (via res.json())
- Code safely accesses response.success before proceeding
- Error handling is in place with try-catch
- After success, code fetches overview endpoint and expects DistributionOverview
Code is correct on frontend side:
- Proper TypeScript typing for response
- Safe response handling with checks
- Proper error handling
What cannot be verified:
- Whether backend endpoint actually exists
- Whether backend returns the exact response shape
- Whether backend implementation matches frontend expectations
[conclusion]
The review comment asks to verify backend endpoint signature, but:
- This is a frontend-only codebase
- Backend implementation is external
- Frontend code is correctly typed and handles responses safely
- The verification request is reasonable but scope is outside frontend codebase
- The code appears to be implementing the contract correctly based on what can be observed
This is not an error in the code - it's a reasonable verification request that cannot be fully satisfied without backend access.
[done]
Verification complete. Frontend code is correct. Backend verification not possible from frontend codebase.
</scratchpad_end] -->Based on my verification, I can now provide the rewritten review comment:
Normalize endpoint appears correctly implemented in frontend; backend verification not possible from frontend codebase.
The frontend code at lines 249-294 correctly implements the endpoint call with proper TypeScript typing. The response shape
{ success: boolean; message?: string; error?: string }is properly typed forcommonApiPost, and the response handling safely checksresponse.successbefore proceeding. The subsequent overview fetch expectsDistributionOverviewwithis_normalizedandphotos_countfields, which aligns with the code at line 248.However, verification of the backend
/api/distributions/${contract}/${tokenId}/normalizeendpoint signature cannot be completed as this is a frontend-only repository and the backend API is external. This verification request should be directed to the backend codebase or API documentation.
183-232: The photo upload API endpoint signature and multipart form handling are implemented correctly.The implementation properly:
- Uses standard multipart FormData with the
"photos"key for multiple file uploads- Calls
commonApiPostForm, which correctly omits theContent-Typeheader (passingfalsetogetHeaders), allowing the browser to automatically setContent-Type: multipart/form-datawith the correct boundary- Includes necessary authentication headers via
getHeaders- Sends FormData directly without modification to
executeApiRequest- Declares the expected response type as
{ success: boolean; photos?: string[]; error?: string }- Handles all error cases (success, error message, exception)
The endpoint
distribution_photos/${contract}/${tokenId}is used consistently for both GET (fetchingDistributionPhoto[]) and POST (uploading photos) operations, which is verified across the codebase.components/user/subscriptions/UserPageSubscriptionsTopUp.tsx (1)
541-582: LGTM! Well-structured option component.The
CardCountOptioncomponent follows React best practices:
- Semantic button element with proper type
- Readonly props as per coding guidelines
- Clean conditional rendering and styling
- Proper accessibility with radio inputs
The component is well-designed for the selection UI pattern used throughout the file.
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscription.tsx (1)
179-188:isNormalizedwarning block is clear and low‑riskThe new
isNormalized‑gated warning is straightforward and clearly communicates that the operation will overwrite existing normalized data. The conditional render is safe (no side effects) and defaults to not rendering when the prop is omitted, which keeps existing call sites behaviorally unchanged.components/distribution/Distribution.tsx (6)
6-14: New NotFound/UpcomingMemePage imports are correctly wired.Both imports are used in this file and the paths match the referenced components; no changes needed here.
134-135: Explicit empty fragment return fromprintDistributionPhotoslooks good.Returning
<></>when there are no photos keeps the return type consistently JSX and matches the always-rendered photos row below.
263-267: Mint countdown gating is safer and more precise now.Conditioning
MemePageMintCountdownonareEqualAddresses(props.contract, MEMES_CONTRACT)plusisValidNftId && nftIdavoids rendering the countdown for non-MEMES contracts or invalid IDs and ensures the parsednft_idis numeric before use. This looks correct.
276-278: Empty state integration withUpcomingMemePagelooks good.Reusing
UpcomingMemePagein the empty state is a nice touch and, combined with theisValidNftIdguard above, ensures it only runs for numeric IDs.
313-315: Early NotFound for invalididis a solid UX improvement.Short-circuiting with
<NotFound label="DISTRIBUTION" />when the URLidis invalid prevents unnecessary fetches and gives a clear 404-style experience for bad routes.
322-333: Layout/spacing changes around the photos row are reasonable.The updated container padding (
tw-pt-6 tw-pb-10) and always-rendered photos row (backed byprintDistributionPhotos()returning an empty fragment when there are no photos) should produce a stable layout with or without photos. Just confirm during QA that the extra vertical space when there are no photos matches the desired design.
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (3)
components/distribution/Distribution.tsx (1)
154-177: UnifygetSpotsForPhaseto returnJSX.Element | nullinstead of mixing strings and JSXRight now
getSpotsForPhasecan return"", a<span>, ornull, which complicates typing and is slightly confusing when consumed in JSX. It would be clearer to always return either a JSX element ornull:- function getSpotsForPhase(d: Distribution, phase: string) { - if (phase.toUpperCase() === "AIRDROP") { - return ""; - } + function getSpotsForPhase( + d: Distribution, + phase: string + ): JSX.Element | null { + if (phase.toUpperCase() === "AIRDROP") { + return null; + } @@ - if (!spotsAirdrop && !spotsAllowlist) { - return ""; - } + if (!spotsAirdrop && !spotsAllowlist) { + return null; + } @@ - } - return null; + } + return null; }This keeps the visual behavior the same but gives you a predictable return type.
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsx (1)
9-19: DeduplicateextractAllNumbersand tighten its typingThis helper still lives locally and is very likely duplicated in
ReviewDistributionPlanTableSubscriptionFooter.tsx. Extracting it to a shared utility (e.g.helpers/Helpers.ts) will remove duplication and make tests/maintenance easier. While doing that, give it explicit types to avoid implicitany.Example:
- function extractAllNumbers(str: string): number[] { - const regex = /\d+/g; - const numbers = []; - let match; - - while ((match = regex.exec(str)) !== null) { - numbers.push(Number.parseInt(match[0], 10)); - } - - return numbers; - } + function extractAllNumbers(str: string): number[] { + const regex = /\d+/g; + const numbers: number[] = []; + let match: RegExpExecArray | null; + + while ((match = regex.exec(str)) !== null) { + numbers.push(Number.parseInt(match[0], 10)); + } + + return numbers; + }Then move this implementation into
helpers/Helpers.ts, export it, and import it here and inReviewDistributionPlanTableSubscriptionFooter.tsxto stay DRY.components/user/subscriptions/UserPageSubscriptionsTopUp.tsx (1)
359-392: Replacedocument.querySelector+setTimeoutwith a ref for the “Other” inputThe “Other” option still uses
document.querySelector('input[placeholder="count"]')andsetTimeoutin bothonClickand radioonChange. This is fragile (coupled to placeholder text and DOM structure), duplicates code, and bypasses React’s ref system. This matches the earlier bot comment and should be refactored.-import { useCallback, useEffect, useState } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; @@ const [error, setError] = useState<string>(""); const [showDeep, setShowDeep] = useState(false); const [mounted, setMounted] = useState(false); const [topUpAmount, setTopUpAmount] = useState<number | null>(null); const [selectedOption, setSelectedOption] = useState<string | null>(null); + const otherInputRef = useRef<HTMLInputElement | null>(null); @@ - <button + <button type="button" @@ - onClick={() => { - setSelectedOption("other"); - setError(""); - setTimeout(() => { - const input = document.querySelector( - 'input[placeholder="count"]' - ) as HTMLInputElement; - input?.focus(); - }, 0); - }}> + onClick={() => { + setSelectedOption("other"); + setError(""); + otherInputRef.current?.focus(); + }}> @@ <Form.Check type="radio" checked={selectedOption === "other"} onChange={() => { setSelectedOption("other"); setError(""); - setTimeout(() => { - const input = document.querySelector( - 'input[placeholder="count"]' - ) as HTMLInputElement; - input?.focus(); - }, 0); + otherInputRef.current?.focus(); }} @@ - <Form.Control + <Form.Control + ref={otherInputRef} type="number"
🧹 Nitpick comments (10)
components/mint-countdown-box/MintCountdownBox.tsx (3)
51-65: Fix creates inconsistency with other status messages.The error state correctly uses FontAwesome (addressing the past review), but now differs from the "Sold Out" (line 70: ✅) and "Mint Phase Complete" (line 82: ⌛️) states which still use emojis. For full consistency with coding guidelines, all status indicators should use FontAwesome.
Additionally, line 57 hardcodes the color
#FFD60Ainstead of using a Tailwind color utility class.Apply this diff to use a Tailwind color class:
<FontAwesomeIcon icon={faExclamationTriangle} - color="#FFD60A" - className="tw-h-6 tw-w-6" + className="tw-h-6 tw-w-6 tw-text-yellow-400" />
67-87: Replace remaining emojis with FontAwesome icons for consistency.The "Sold Out" and "Mint Phase Complete" states still use emojis (✅ and ⌛️) while the error state now uses FontAwesome. Per coding guidelines, all icons should be FontAwesome components.
Consider applying this pattern to use FontAwesome icons throughout:
+import { faCheckCircle, faClock } from "@fortawesome/free-solid-svg-icons"; if (mintInfo?.isSoldOut) { return ( <div className={containerClasses}> - <h4 className="mb-3">✅ Mint Complete - Sold Out!</h4> + <h4 className="mb-3 tw-flex tw-items-center tw-gap-2"> + <FontAwesomeIcon icon={faCheckCircle} className="tw-h-6 tw-w-6 tw-text-green-500" /> + Mint Complete - Sold Out! + </h4> <p className="mb-1 tw-font-medium"> All NFTs have been successfully minted. </p> <p className="mb-0 tw-text-md">Thank you for participating!</p> </div> ); } if (mintInfo?.isFinalized) { return ( <div className={containerClasses}> - <h4 className="mb-3">⌛️ Mint Phase Complete</h4> + <h4 className="mb-3 tw-flex tw-items-center tw-gap-2"> + <FontAwesomeIcon icon={faClock} className="tw-h-6 tw-w-6 tw-text-gray-400" /> + Mint Phase Complete + </h4> <p className="mb-1 tw-font-medium">This mint phase has ended.</p> <p className="mb-0 tw-text-md">Thank you for participating!</p> </div> ); }
32-89: Remove code comments per coding guidelines.The coding guidelines specify: "Do not include any comments in the code; it should be self-explanatory." Please remove comments on lines 32, 38, and 89.
Apply this diff:
- // Derived flags const hasMintInfo = Boolean(mintInfo); - // Safe date conversion (only when present) const countdownDate = - // Default: minting state return (Based on coding guidelines, the code should be self-explanatory through clear variable and function names.
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTable.tsx (1)
90-105: Public phase logic is correct.The admin-gated public phase row is properly structured and conditionally added based on user permissions.
Consider extracting the hardcoded strings "Public" and "Auto-generated" to named constants for better maintainability and potential i18n support.
For example:
const PUBLIC_PHASE_NAME = "Public"; const PUBLIC_PHASE_DESCRIPTION = "Auto-generated";components/distribution/Distribution.tsx (2)
6-6: TightenisValidNftIdso only purely numeric ids are acceptedThe early
NotFoundguard is helpful, butparseIntwill treat values like"123abc"as valid, which could unintentionally map to another card. If you want the route id to be strictly numeric, consider validating the full string:- const isValidNftId = !Number.isNaN(Number.parseInt(params?.id as string, 10)); - const nftId = isValidNftId ? (params?.id as string) : ""; + const routeId = (params?.id as string) ?? ""; + const isValidNftId = /^\d+$/.test(routeId); + const nftId = isValidNftId ? routeId : "";This keeps the
NotFoundbehavior but ensures only digit-only ids are treated as valid.Also applies to: 43-44, 318-320
242-246: Avoid making<td>a flex container; wrap content in a flex child insteadApplying Tailwind’s
tw-flexdirectly to<td>changes its display fromtable-celltoflex, which can interfere with table layout and column sizing. It is safer to keep the cell as a table-cell and flex only the inner content:- {distributionsPhases.map((p) => ( - <td - key={`${p}-${d.wallet}`} - className="tw-flex tw-justify-center tw-items-center"> - {getCountForPhase(d, p)} - {getSpotsForPhase(d, p)} - </td> - ))} + {distributionsPhases.map((p) => { + const spots = getSpotsForPhase(d, p); + return ( + <td + key={`${p}-${d.wallet}`} + className="tw-text-center"> + <span className="tw-inline-flex tw-items-center tw-justify-center"> + <span>{getCountForPhase(d, p)}</span> + {spots && <span className="tw-ml-2">{spots}</span>} + </span> + </td> + ); + })}This preserves the alignment you want while keeping proper table semantics.
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsx (1)
157-160: Use FontAwesome + Tailwind for the warning instead of a raw emoji + Bootstrap alertThe warning block currently mixes a literal emoji with Bootstrap classes:
<div className="alert alert-warning mb-0 border border-dark"> ⚠️ {props.existingPhotosCount} photo(s) already exist. This will replace all existing photos. </div>To align with the guidelines (FontAwesome for icons, Tailwind for styling), consider switching to a FontAwesome icon and Tailwind utility classes, for example:
<div className="tw-mb-0 tw-flex tw-items-center tw-gap-2 tw-rounded tw-border tw-border-yellow-500 tw-bg-yellow-100 tw-p-2 tw-text-yellow-900"> <FontAwesomeIcon icon={faTriangleExclamation} /> <span> {props.existingPhotosCount} photo(s) already exist. This will replace all existing photos. </span> </div>Remember to add the necessary FontAwesome imports at the top of the file if they are not already present.
components/user/subscriptions/UserPageSubscriptionsTopUp.tsx (3)
126-138: SurfacewaitSendTransaction.errorso reverted/dropped txs also show an error modalRight now only
sendTransaction.erroris mapped toerror, so failures duringwaitSendTransaction(e.g. tx revert or dropped/replaced) will silently clear the modal with no explanation. Mirror the same handling forwaitSendTransaction.errorso all tx failures are visible and closable via the existing modal.useEffect(() => { if (sendTransaction.error) { const errorMsg = sendTransaction.error.message.split("Request Arguments")[0]; setError(`Error - ${errorMsg}`); } }, [sendTransaction.error]); + + useEffect(() => { + if (waitSendTransaction.error) { + const errorMsg = + waitSendTransaction.error.message.split("Request Arguments")[0]; + setError(`Error - ${errorMsg}`); + } + }, [waitSendTransaction.error]);
254-265: Avoid nesting a<button>insidenext/linkfor external navigationFor the iOS CTA you’re using
next/link(which renders an<a>) wrapping a<button>, which is invalid interactive nesting and doesn’t buy you much for an externalhref. Given the guideline is to use<Link>for internal routes, it’s cleaner to use a styled<a>here.- const iOsContent = ( - <Row className="pt-2"> - <Col> - <Link - href={window.location.href} - className="text-center pt-2 pb-2" - target="_blank" - rel="noopener noreferrer"> - <button className="btn btn-light" style={{ width: "100%" }}> - Top-up on 6529.io - </button> - </Link> - </Col> - </Row> - ); + const iOsContent = ( + <Row className="pt-2"> + <Col> + <a + href={window.location.href} + className="btn btn-light text-center pt-2 pb-2" + style={{ width: "100%" }} + target="_blank" + rel="noopener noreferrer"> + Top-up on 6529.io + </a> + </Col> + </Row> + );
471-479: Tooltip shows the identifier name instead of the actual subscription addressThe tooltip currently renders the literal string
SUBSCRIPTIONS_ADDRESS, which is less useful than the actual address value. Consider using the constant so the user sees the real contract address on hover.- <Tooltip + <Tooltip id="subscription-address" style={{ backgroundColor: "#1F2937", color: "white", padding: "4px 8px", }}> - SUBSCRIPTIONS_ADDRESS + {SUBSCRIPTIONS_ADDRESS} </Tooltip>
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTable.tsx(2 hunks)components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsx(1 hunks)components/distribution/Distribution.tsx(9 hunks)components/mint-countdown-box/MemePageMintCountdown.tsx(3 hunks)components/mint-countdown-box/MintCountdownBox.tsx(3 hunks)components/user/subscriptions/UserPageSubscriptionsTopUp.tsx(6 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- components/mint-countdown-box/MemePageMintCountdown.tsx
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx,js,jsx}: Do not include any comments in the code; it should be self-explanatory
Write correct, up-to-date, bug-free, fully componentized, secure, and efficient code
Include all required imports and ensure proper naming of key components
Use NextJS features that match the current version
**/*.{ts,tsx,js,jsx}: Enforce ≥ 80% line coverage for files changed sincemainvianpm run test
All code must pass ESLint (Next's Core Web Vitals + React Hooks rules) vianpm run lint
Use<Link>from Next.js for internal navigation instead of<a>tags or HTML anchors
Use<Image>fromnext/imageinstead of HTML<img>elements
Files:
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsxcomponents/mint-countdown-box/MintCountdownBox.tsxcomponents/distribution/Distribution.tsxcomponents/user/subscriptions/UserPageSubscriptionsTopUp.tsxcomponents/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTable.tsx
**/*.{tsx,jsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{tsx,jsx}: Use FontAwesome for icons in React components
Use TailwindCSS for styling in React components
Use react-query for data fetching
Always addreadonlybefore props in React components
Files:
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsxcomponents/mint-countdown-box/MintCountdownBox.tsxcomponents/distribution/Distribution.tsxcomponents/user/subscriptions/UserPageSubscriptionsTopUp.tsxcomponents/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTable.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: All code must pass TypeScript type checking vianpm run type-check(tsc --noEmit)
Use'use cache'directive at the top of Server Components or functions to explicitly opt-in to caching in Next.js 16
Files:
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsxcomponents/mint-countdown-box/MintCountdownBox.tsxcomponents/distribution/Distribution.tsxcomponents/user/subscriptions/UserPageSubscriptionsTopUp.tsxcomponents/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTable.tsx
🧠 Learnings (4)
📚 Learning: 2025-11-25T08:35:58.721Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T08:35:58.721Z
Learning: Applies to **/*.{tsx,jsx} : Use FontAwesome for icons in React components
Applied to files:
components/mint-countdown-box/MintCountdownBox.tsxcomponents/user/subscriptions/UserPageSubscriptionsTopUp.tsx
📚 Learning: 2025-11-25T08:37:14.939Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T08:37:14.939Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use `<Link>` from Next.js for internal navigation instead of `<a>` tags or HTML anchors
Applied to files:
components/mint-countdown-box/MintCountdownBox.tsx
📚 Learning: 2025-11-25T08:37:14.939Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T08:37:14.939Z
Learning: Fix issues with modernization aligned to React 19.2, React Compiler, and Next.js 16 conventions; do not add `// eslint-disable` comments unless explicitly instructed
Applied to files:
components/distribution/Distribution.tsxcomponents/user/subscriptions/UserPageSubscriptionsTopUp.tsx
📚 Learning: 2025-11-25T08:37:14.939Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T08:37:14.939Z
Learning: Use TypeScript and React functional components with hooks
Applied to files:
components/user/subscriptions/UserPageSubscriptionsTopUp.tsxcomponents/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTable.tsx
🧬 Code graph analysis (1)
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsx (5)
scripts/dev-open.cjs (1)
match(43-43)components/allowlist-tool/allowlist-tool.types.ts (1)
AllowlistDescription(12-18)constants.ts (1)
MEMES_CONTRACT(5-5)__mocks__/react-use.js (1)
React(2-2)helpers/Helpers.ts (1)
formatAddress(31-46)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (9)
components/mint-countdown-box/MintCountdownBox.tsx (2)
1-4: Good addition of FontAwesome icon import.The import of
faExclamationTriangleproperly addresses the previous review feedback to use FontAwesome icons instead of emojis for the error state.
11-30: LGTM! Props and destructuring are correctly implemented.The optional
isErrorprop is properly typed, and the component correctly usesReadonly<Props>as required by coding guidelines.components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTable.tsx (4)
3-11: LGTM! Imports are correctly organized.All new imports are necessary and properly structured for the new admin-gated public phase functionality.
108-108: LGTM! useEffect dependencies are correct.Both
phasesandconnectedProfileare properly included in the dependency array, ensuring the effect runs when either value changes.
51-86: I'll help you verify the phase description concern in the review comment. Let me search the codebase to understand the phase object structure and whether the description field should be populated.
<function_calls>
#!/bin/bashFind the file under review
fd -t f "ReviewDistributionPlanTable.tsx"
</function_calls>
<function_calls>
#!/bin/bashSearch for ReviewDistributionPlanTableItem interface/type definition
rg -n "ReviewDistributionPlanTableItem" --type ts --type tsx -A 15
</function_calls>
<function_calls>
#!/bin/bashSearch for phase type definitions in the component files
rg -n "interface.*Phase|type.*Phase" --type ts --type tsx -A 10 | head -100
</function_calls>
46-46: Perfect! I found the implementation. TheisSubscriptionsAdminfunction is properly designed to handle null/undefined values:export const isSubscriptionsAdmin = (connectedProfile: ApiIdentity | null) => { const connectedWallets = connectedProfile?.wallets?.map((wallet) => wallet.wallet) ?? []; return connectedWallets.some((w) => SUBSCRIPTIONS_ADMIN_WALLETS.some((a) => areEqualAddresses(a, w)) ); };Key safety mechanisms:
- Parameter type explicitly allows
null(ApiIdentity | null)- Uses optional chaining (
?.) to safely access nested properties- Uses nullish coalescing (
??) to default to an empty array if the chain breaks.some()on an empty array safely returnsfalseNo runtime errors will occur when
connectedProfileis null or undefined. The original review comment's concern is not valid - proper null-safety is already in place.components/distribution/Distribution.tsx (2)
268-272: Minting link guard now correctly enforces contract + numeric idThe updated condition in
printMintingLink(checkingMEMES_CONTRACT,isValidNftId, andnftId) combined withNumber.parseInt(nftId, 10)is a solid improvement over the previous behavior and avoids rendering the countdown when the route id is invalid or the contract does not match.
13-13: UpcomingMemePage integration in the empty state looks consistentImporting and rendering
UpcomingMemePageinprintEmptywired toid={nftId}fits the new empty-state UX, and thanks to the earlyNotFoundguard it will only run when the id is considered valid.Also applies to: 281-283
components/user/subscriptions/UserPageSubscriptionsTopUp.tsx (1)
458-537: Modal +CardCountOptionimplementation looks solid and aligns with guidelinesThe modal portal is correctly gated behind
mountedto avoiddocument.bodySSR issues, the animation/overlay logic is clean, and the top‑up summary text now renders with proper spacing.CardCountOptionusesReadonlyprops, Tailwind, and radio semantics appropriately, and the pluralization/ETH math reads clearly. This matches the React‑with‑hooks and FontAwesome/Tailwind expectations from the learnings.Also applies to: 542-582
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (2)
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsx (1)
18-20: HardentokenIdvalidation to only accept positive integer IDs and reuse it across the component.Right now
parseInt-based checks still treat"1.5"or"1abc"as valid (becauseparseIntstops at the first non‑digit). This can let malformed token IDs through, and the same logic is duplicated in both the upload guard and the button’sdisabledstate. Also, addingstep={1}helps discourage fractional values in the UI. This aligns with the earlier review feedback on this flow.You can centralize validation and tighten the checks like this:
const [tokenId, setTokenId] = useState<string>( extractAllNumbers(props.plan.name)[0]?.toString() ?? "" ); + const isValidTokenId = (value: string): boolean => + /^[1-9]\d*$/.test(value.trim()); @@ const handleUpload = () => { @@ - if (!tokenId || Number.isNaN(Number.parseInt(tokenId, 10))) { + if (!isValidTokenId(tokenId)) { return; } @@ <input @@ - min={1} - type="number" - value={tokenId} + min={1} + step={1} + type="number" + value={tokenId} onChange={(e) => { setTokenId(e.target.value); }} @@ <Button - disabled={ - !tokenId || - Number.isNaN(Number.parseInt(tokenId, 10)) || - selectedFiles.length === 0 - } + disabled={!isValidTokenId(tokenId) || selectedFiles.length === 0}Also applies to: 88-90, 127-138, 207-211
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscription.tsx (1)
135-138: FixtokenIdinput binding and tighten validation to only allow positive integer IDs.
value={Number.parseInt(tokenId, 10)}will beNaNwhentokenIdis"", which leads to React warnings and awkward UX on the number input. TheparseInt-based disabled check also allows malformed values like"1.5"or"1abc". This is the same pattern previously called out on related components and should be unified here as well.You can keep
tokenIdas a string, validate it via a helper, and reuse that helper for the disabled state:export function SubscriptionConfirm( @@ const contract = MEMES_CONTRACT; const numbers = extractAllNumbers(props.plan.name); const [tokenId, setTokenId] = useState<string>( numbers.length > 0 ? numbers[0].toString() : "" ); + const isValidTokenId = (value: string): boolean => + /^[1-9]\d*$/.test(value.trim()); @@ <input @@ min={1} - type="number" - value={Number.parseInt(tokenId, 10)} + type="number" + step={1} + value={tokenId} onChange={(e) => { setTokenId(e.target.value); }} @@ </Button> <Button - disabled={!tokenId || Number.isNaN(Number.parseInt(tokenId, 10))} + disabled={!isValidTokenId(tokenId)} variant="primary" onClick={() => props.onConfirm(contract, tokenId)}>Also applies to: 157-169, 188-191
🧹 Nitpick comments (5)
components/user/subscriptions/UserPageSubscriptionsTopUp.tsx (1)
550-556: Radio button inside button causes double handler invocation.When clicking directly on the radio input, both the radio's
onChangeand the wrapping button'sonClickfire, callingprops.onSelecttwice. While harmless since the operation is idempotent, you could prevent the redundant call.<button type="button" className={`tw-border tw-rounded-lg tw-p-3 tw-cursor-pointer tw-transition-colors tw-w-full tw-text-left ${ styles.cardCountOption } ${ props.selected ? "tw-bg-iron-700" : "tw-bg-transparent hover:tw-bg-iron-900" }`} onClick={props.onSelect}> <Row className="d-flex align-items-center"> <Col xs={1} className="d-flex tw-justify-center"> <Form.Check type="radio" checked={props.selected} - onChange={props.onSelect} + onChange={() => {}} style={{ accentColor: "#406AFE", width: "20px", height: "20px", }} className={`tw-scale-125 ${styles.radioButton}`} />components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsx (1)
141-151: Consider replacing the literal warning emoji with a FontAwesome icon for consistency.Given the project’s styling guidelines for TSX/JSX, you may want to swap the raw
⚠️marker and text-only remove actions for FontAwesome icons (e.g., an exclamation/triangle and a trash/remove icon) to keep iconography consistent across the app.Also applies to: 176-199
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx (2)
20-37: Clearoverviewwhen no valid distribution/token is available to avoid stale UI.If
distributionPlanbecomes invalid or itsnameno longer yields a numeric tokenId, the effect early‑returns but leaves the previousoverviewin state. That can show an out‑of‑date photo count/normalized flag for the new plan. You can reset the overview in the guard paths:useEffect(() => { async function fetchOverview() { - if (!distributionPlan || !isSubscriptionsAdmin(connectedProfile)) { - return; - } + if (!distributionPlan || !isSubscriptionsAdmin(connectedProfile)) { + setOverview(null); + return; + } @@ - const tokenId = extractAllNumbers(distributionPlan.name)[0]?.toString(); - - if (!tokenId) { - return; - } + const tokenId = extractAllNumbers(distributionPlan.name)[0]?.toString(); + + if (!tokenId) { + setOverview(null); + return; + }Also applies to: 38-62
143-164: Swap the normalization status emojis for FontAwesome icons to align with icon guidelines.Using literal
"✅"/"❌"works visually but diverges from a FontAwesome-based icon system. Consider substituting with appropriate FontAwesome icons (e.g., check/times or check/circle variants) for better consistency and theming control.Also applies to: 155-160
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscription.tsx (1)
219-242:download/CSV processing logic is sound; consider simplifying the async chain later.The
downloadfunction +processResultscorrectly map the API response into multiple CSV downloads, and the special handling for the"public"phase name is clear. At some point you might want to replace theawait ... .then().catch()combination with a straighttry/catchfor readability, but behavior-wise this is fine as-is.Also applies to: 244-258
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscription.tsx(5 hunks)components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx(4 hunks)components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsx(1 hunks)components/user/subscriptions/UserPageSubscriptionsTopUp.tsx(6 hunks)helpers/Helpers.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx,js,jsx}: Do not include any comments in the code; it should be self-explanatory
Write correct, up-to-date, bug-free, fully componentized, secure, and efficient code
Include all required imports and ensure proper naming of key components
Use NextJS features that match the current version
**/*.{ts,tsx,js,jsx}: Enforce ≥ 80% line coverage for files changed sincemainvianpm run test
All code must pass ESLint (Next's Core Web Vitals + React Hooks rules) vianpm run lint
Use<Link>from Next.js for internal navigation instead of<a>tags or HTML anchors
Use<Image>fromnext/imageinstead of HTML<img>elements
Files:
helpers/Helpers.tscomponents/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsxcomponents/user/subscriptions/UserPageSubscriptionsTopUp.tsxcomponents/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsxcomponents/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscription.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: All code must pass TypeScript type checking vianpm run type-check(tsc --noEmit)
Use'use cache'directive at the top of Server Components or functions to explicitly opt-in to caching in Next.js 16
Files:
helpers/Helpers.tscomponents/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsxcomponents/user/subscriptions/UserPageSubscriptionsTopUp.tsxcomponents/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsxcomponents/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscription.tsx
**/*.{tsx,jsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{tsx,jsx}: Use FontAwesome for icons in React components
Use TailwindCSS for styling in React components
Use react-query for data fetching
Always addreadonlybefore props in React components
Files:
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsxcomponents/user/subscriptions/UserPageSubscriptionsTopUp.tsxcomponents/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsxcomponents/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscription.tsx
🧠 Learnings (3)
📚 Learning: 2025-11-25T08:37:14.939Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T08:37:14.939Z
Learning: Fix issues with modernization aligned to React 19.2, React Compiler, and Next.js 16 conventions; do not add `// eslint-disable` comments unless explicitly instructed
Applied to files:
components/user/subscriptions/UserPageSubscriptionsTopUp.tsx
📚 Learning: 2025-11-25T08:35:58.721Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T08:35:58.721Z
Learning: Applies to **/*.{tsx,jsx} : Use FontAwesome for icons in React components
Applied to files:
components/user/subscriptions/UserPageSubscriptionsTopUp.tsx
📚 Learning: 2025-11-25T08:37:14.939Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T08:37:14.939Z
Learning: Use TypeScript and React functional components with hooks
Applied to files:
components/user/subscriptions/UserPageSubscriptionsTopUp.tsx
🧬 Code graph analysis (2)
components/user/subscriptions/UserPageSubscriptionsTopUp.tsx (4)
constants.ts (4)
MEMES_MINT_PRICE(74-74)SUBSCRIPTIONS_CHAIN(61-61)SUBSCRIPTIONS_ADDRESS_ENS(67-67)SUBSCRIPTIONS_ADDRESS(65-66)components/distribution-plan-tool/common/CircleLoader.tsx (1)
CircleLoader(9-39)helpers/Helpers.ts (3)
getTransactionLink(237-246)formatAddress(31-46)numberWithCommasFromString(72-81)components/show-more-button/ShowMoreButton.tsx (1)
ShowMoreButton(7-35)
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsx (3)
components/allowlist-tool/allowlist-tool.types.ts (1)
AllowlistDescription(12-18)helpers/Helpers.ts (2)
extractAllNumbers(83-93)formatAddress(31-46)constants.ts (1)
MEMES_CONTRACT(5-5)
🔇 Additional comments (8)
components/user/subscriptions/UserPageSubscriptionsTopUp.tsx (3)
44-89: LGTM on state management and ref-based focus handling.The state declarations and
handleSelectOthercallback properly address the previous review feedback by usinguseRefinstead ofdocument.querySelector. Themountedstate correctly guards against SSR issues with the portal.
149-183: LGTM on modal lifecycle management.The body scroll lock, escape key handler, and
closeModalcallback are properly implemented with appropriate cleanup in theuseEffectreturns. UsingglobalThis.addEventListeneris SSR-safe.
185-255: LGTM on modal content rendering.The
getModalContentfunction appropriately handles all transaction states with clear visual feedback. External etherscan links correctly use<a>tags withtarget="_blank"andrel="noopener noreferrer".components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsx (1)
27-56: File validation and reset behavior look solid.Type/size checks, clear error messaging, and resetting both
selectedFilesand the underlying<input>cover the common UX pitfalls for multi-file uploads nicely.Also applies to: 58-81
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx (1)
185-203: Upload flow + overview refetch is well-structured.The upload callback cleanly manages modal visibility, loading state, success/error toasts, and a follow-up overview refetch; this keeps the footer UI in sync with backend photo state.
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscription.tsx (3)
44-52: Admin + PHASE gating and public vs non‑public button split look good.Early‑out when not subscriptions admin or not a PHASE, plus the
isPublicbranch that changes labels/titles, is a clear way to separate public and private subscription flows without complicating the parent table.Also applies to: 53-121
172-181: Normalized-warning block is a useful guardrail.The conditional warning when
isNormalizedis true gives admins an explicit heads-up that re-running normalization will overwrite existing data, which should reduce accidental recalculations.
211-217: Subscriptions admin check is well-scoped.Using
SUBSCRIPTIONS_ADMIN_WALLETSplusareEqualAddressesagainst all connected wallets is a clear, case-insensitive admin gate and keeps this concern nicely localized.
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
components/user/subscriptions/UserPageSubscriptionsTopUp.tsx (1)
461-469: LGTM!The tooltip now correctly displays the actual subscription address value using
{SUBSCRIPTIONS_ADDRESS}instead of the literal string.
🧹 Nitpick comments (4)
components/distribution/Distribution.tsx (1)
154-177: Consider unifying return types for consistency.The function returns
""(empty string) on lines 156 and 165, butnullon line 176. While React renders both as nothing, returningnullconsistently would be cleaner for type inference.function getSpotsForPhase(d: Distribution, phase: string) { if (phase.toUpperCase() === "AIRDROP") { - return ""; + return null; } const p = d.allowlist.find((a) => a.phase === phase); const count = p?.spots ?? 0; if (count > 0) { const spotsAirdrop = p?.spots_airdrop ?? 0; const spotsAllowlist = p?.spots_allowlist ?? 0; if (!spotsAirdrop && !spotsAllowlist) { - return ""; + return null; }components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsx (1)
172-179: Consider using a more stable key for error messages.Using the error string as a key works for unique messages but could cause issues if duplicate error messages occur. Consider using an index-based key.
- {fileErrors.map((error) => ( - <div key={error} className="text-danger"> + {fileErrors.map((error, index) => ( + <div key={index} className="text-danger"> {error} </div> ))}components/user/subscriptions/UserPageSubscriptionsTopUp.tsx (2)
532-573: Prevent potential double-firing of onSelect.Both the outer
<button>onClick(line 550) and the innerForm.CheckonChange(line 556) callprops.onSelect. When clicking the radio button, the event bubbles to the parent button, potentially callingonSelecttwice.<Form.Check type="radio" checked={props.selected} - onChange={props.onSelect} + onChange={(e) => e.stopPropagation()} style={{ accentColor: "#406AFE", width: "20px", height: "20px", }} className={`tw-scale-125 ${styles.radioButton}`} />The parent button already handles the click, so the radio's
onChangecan just stop propagation. Thecheckedprop will keep it in sync.
376-388: Same double-firing concern as CardCountOption.The "Other" option button (line 375) and its radio (line 381) both call
handleSelectOther. Apply the same fix to prevent double execution.<Form.Check type="radio" checked={selectedOption === "other"} - onChange={handleSelectOther} + onChange={(e) => e.stopPropagation()} style={{
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsx(1 hunks)components/distribution/Distribution.tsx(9 hunks)components/user/subscriptions/UserPageSubscriptionsTopUp.tsx(6 hunks)helpers/Helpers.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx,js,jsx}: Do not include any comments in the code; it should be self-explanatory
Write correct, up-to-date, bug-free, fully componentized, secure, and efficient code
Include all required imports and ensure proper naming of key components
Use NextJS features that match the current version
**/*.{ts,tsx,js,jsx}: Enforce ≥ 80% line coverage for files changed sincemainvianpm run test
All code must pass ESLint (Next's Core Web Vitals + React Hooks rules) vianpm run lint
Use<Link>from Next.js for internal navigation instead of<a>tags or HTML anchors
Use<Image>fromnext/imageinstead of HTML<img>elements
Files:
helpers/Helpers.tscomponents/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsxcomponents/distribution/Distribution.tsxcomponents/user/subscriptions/UserPageSubscriptionsTopUp.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: All code must pass TypeScript type checking vianpm run type-check(tsc --noEmit)
Use'use cache'directive at the top of Server Components or functions to explicitly opt-in to caching in Next.js 16
Files:
helpers/Helpers.tscomponents/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsxcomponents/distribution/Distribution.tsxcomponents/user/subscriptions/UserPageSubscriptionsTopUp.tsx
**/*.{tsx,jsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{tsx,jsx}: Use FontAwesome for icons in React components
Use TailwindCSS for styling in React components
Use react-query for data fetching
Always addreadonlybefore props in React components
Files:
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsxcomponents/distribution/Distribution.tsxcomponents/user/subscriptions/UserPageSubscriptionsTopUp.tsx
🧠 Learnings (4)
📚 Learning: 2025-11-25T08:37:14.939Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T08:37:14.939Z
Learning: Fix issues with modernization aligned to React 19.2, React Compiler, and Next.js 16 conventions; do not add `// eslint-disable` comments unless explicitly instructed
Applied to files:
components/distribution/Distribution.tsxcomponents/user/subscriptions/UserPageSubscriptionsTopUp.tsx
📚 Learning: 2025-11-25T08:37:14.939Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T08:37:14.939Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use `<Image>` from `next/image` instead of HTML `<img>` elements
Applied to files:
components/distribution/Distribution.tsx
📚 Learning: 2025-11-25T08:35:58.721Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T08:35:58.721Z
Learning: Applies to **/*.{tsx,jsx} : Use FontAwesome for icons in React components
Applied to files:
components/user/subscriptions/UserPageSubscriptionsTopUp.tsx
📚 Learning: 2025-11-25T08:37:14.939Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T08:37:14.939Z
Learning: Use TypeScript and React functional components with hooks
Applied to files:
components/user/subscriptions/UserPageSubscriptionsTopUp.tsx
🧬 Code graph analysis (3)
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsx (3)
components/allowlist-tool/allowlist-tool.types.ts (1)
AllowlistDescription(12-18)helpers/Helpers.ts (3)
extractAllNumbers(83-93)isValidPositiveInteger(95-109)formatAddress(31-46)constants.ts (1)
MEMES_CONTRACT(5-5)
components/distribution/Distribution.tsx (4)
helpers/Helpers.ts (2)
numberWithCommas(111-128)areEqualAddresses(177-182)constants.ts (1)
MEMES_CONTRACT(5-5)components/the-memes/UpcomingMemePage.tsx (1)
UpcomingMemePage(4-14)components/not-found/NotFound.tsx (1)
NotFound(7-51)
components/user/subscriptions/UserPageSubscriptionsTopUp.tsx (1)
constants.ts (4)
MEMES_MINT_PRICE(74-74)SUBSCRIPTIONS_CHAIN(61-61)SUBSCRIPTIONS_ADDRESS_ENS(67-67)SUBSCRIPTIONS_ADDRESS(65-66)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (13)
helpers/Helpers.ts (2)
83-93: LGTM!The
extractAllNumbersfunction is correctly implemented. Thematchvariable is now properly typed asRegExpExecArray | null, addressing the previous review comment.
95-109: LGTM!The
isValidPositiveIntegerfunction properly validates both number and string inputs, correctly rejecting edge cases like decimals ("1.5"), leading zeros ("01"), and mixed strings ("1abc"). Thenum.toString() === value.trim()check ensures strict validation.components/distribution/Distribution.tsx (4)
43-44: LGTM!The validation logic correctly uses
Number.parseIntwith radix 10 to determine if the route parameter is a valid numeric ID. The derivednftIddefaults to an empty string when invalid, preventing downstream issues.
110-139: LGTM!The carousel implementation correctly limits image preloading to the first slide with
priority={index === 0}, and thewidth/heightprops are now properly typed as numbers. The empty fragment return for no photos is acceptable.
278-308: LGTM!The empty state now correctly integrates
UpcomingMemePagewith the nftId. The external link to X/Twitter appropriately uses an<a>tag with proper security attributes (target="_blank"andrel="noopener noreferrer").
318-320: LGTM!Clean early return pattern for invalid NFT IDs, correctly rendering the
NotFoundcomponent with an appropriate label.components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsx (3)
22-26: LGTM!The token ID extraction correctly uses the shared
extractAllNumbershelper and validates the initial value withisValidPositiveInteger. This addresses previous review comments about DRY principles and proper validation.
33-62: LGTM!File validation is well-implemented with efficient
Setlookups for MIME type checking and comprehensive error messaging for both type and size violations.
88-104: LGTM!The upload handler properly validates both file selection and token ID before proceeding. Using
isValidPositiveIntegerensures robust validation that rejects malformed values.components/user/subscriptions/UserPageSubscriptionsTopUp.tsx (4)
76-89: LGTM!State management is well-structured. The
useRefpattern correctly replaces the fragiledocument.querySelectorapproach from previous code.
91-131: LGTM!The
handleSendfunction correctly validates the selected option, computes the transaction value, and handles both predefined and custom count options.
149-183: LGTM!The modal lifecycle is well-managed with proper body scroll locking, escape key handling, and cleanup. The
closeModalcallback correctly resets all relevant state.
185-255: LGTM!The
getModalContentfunction provides clear visual feedback for all transaction states (error, pending confirmation, submitted/loading, success) with appropriate loading indicators and transaction links.
Signed-off-by: prxt6529 <prxt@6529.io>
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (3)
components/user/subscriptions/UserPageSubscriptions.module.scss (1)
152-168: Focus indicator concern has been addressed.The
:focus-visiblepseudo-class now provides a visible outline for keyboard navigation while:focus:not(:focus-visible)hides it for mouse interactions. This meets WCAG 2.4.7 Focus Visible requirements.components/user/subscriptions/UserPageSubscriptionsTopUp.tsx (2)
461-469: Tooltip issue has been addressed.The tooltip now correctly displays the actual
{SUBSCRIPTIONS_ADDRESS}value instead of the literal variable name.
500-504: Text spacing issue has been addressed.The display text now correctly includes proper spacing between the card count and "Cards".
🧹 Nitpick comments (4)
components/user/subscriptions/UserPageSubscriptionsTopUp.tsx (4)
554-560: Potential double event trigger when clicking the radio directly.When the user clicks directly on the radio input, both the
onChangehandler and the button'sonClickhandler will fire (due to event bubbling), causingonSelectto be called twice. While this may be benign for simple state toggles, it can cause unexpected behavior.Consider either stopping propagation or removing the radio's
onChange:🔎 Suggested fix
<input type="radio" checked={props.selected} - onChange={props.onSelect} + onChange={() => {}} aria-label={labelText} className={styles.radioInput} />
382-388: Same double-trigger pattern as CardCountOption.The radio's
onChangeand the parent button'sonClickboth callhandleSelectOther. For consistency with any fix applied toCardCountOption, consider the same approach here.
481-492: Consider adding focus trap for modal accessibility.The modal implementation has good features (escape key handling, backdrop click, aria-label on close button), but it lacks focus trapping. For full keyboard accessibility, focus should be contained within the modal while it's open.
Consider using a library like
@radix-ui/react-dialogorreact-focus-lockto handle focus trapping, or implement it manually by focusing the first interactive element on open and trapping Tab/Shift+Tab navigation.
281-304: Minor: Consider renaming or extractingprintRemainingMints.This function returns JSX and acts like a component. Consider either:
- Extracting it as a proper component (
RemainingMintsOption)- Renaming to
renderRemainingMintsto follow React conventions for render methodsThis is a minor naming convention suggestion.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
components/user/subscriptions/UserPageSubscriptions.module.scss(2 hunks)components/user/subscriptions/UserPageSubscriptionsTopUp.tsx(6 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx,js,jsx}: Do not include any comments in the code; it should be self-explanatory
Write correct, up-to-date, bug-free, fully componentized, secure, and efficient code
Include all required imports and ensure proper naming of key components
Use NextJS features that match the current version
**/*.{ts,tsx,js,jsx}: Replace<img>elements with<Image />fromnext/imageto satisfy@next/next/no-img-elementESLint rule
Use<Link href="/path">from Next.js for internal navigation instead of plain HTML links to satisfy@next/next/no-html-link-for-pagesESLint rule
Files:
components/user/subscriptions/UserPageSubscriptionsTopUp.tsx
**/*.{tsx,jsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{tsx,jsx}: Use FontAwesome for icons in React components
Use TailwindCSS for styling in React components
Use react-query for data fetching
Always addreadonlybefore props in React components
Files:
components/user/subscriptions/UserPageSubscriptionsTopUp.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks)
Use framework APIs: internal links should use<Link>, images should usenext/image, and adopt Next's ESLint rules (Core Web Vitals)
**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks rules)
Follow existing code style and naming conventions; maintain clean code standards (measured by SonarQube)
Files:
components/user/subscriptions/UserPageSubscriptionsTopUp.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Must passtsc --noEmittype checking
Prefer direct named imports for React hooks and types (import { useMemo, useRef, FC, etc. } from "react") overReact.namespace usage (React.useMemo,React.useRef, etc.)
If thereact-hooks/exhaustive-depslint rule is triggered: if the Effect only derives state, remove the Effect and compute during render; if listening to an external system and needing fresh props/state, wrap non-reactive logic inuseEffectEvent
**/*.{ts,tsx}: Must passtsc --noEmitfor TypeScript type checking
Prefer Server Components over Client Components; use Server Functions/Server Actions ('use server') for mutations
Remove unnecessary Effects; if Effect only derives state, compute during render instead
UseuseEffectEventfor non-reactive logic inside Effects to avoid unnecessary re-runs
Use framework APIs:<Link>for internal links,next/imagefor images, adopt Next's ESLint rules
Use'use cache'directive and Cache Components features for explicit opt-in caching in Next.js 16
Use TypeScript and React functional components with hooks
When parsing Seize URLs or similar, fail fast if base origin is unavailable; do not fall back to placeholder origins
Replace<img>elements with<Image />fromnext/image
Use<Link href="/path">for internal navigation instead of plain HTML links
Move data fetches to Server Components; handle mutations through Server Functions/Server Actions with'use server'directive
Files:
components/user/subscriptions/UserPageSubscriptionsTopUp.tsx
🧠 Learnings (4)
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/__tests__/**/components/**/*.test.{ts,tsx} : Test accessibility with keyboard navigation and screen reader compatibility
Applied to files:
components/user/subscriptions/UserPageSubscriptions.module.scss
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/{components,contexts,hooks}/**/*.{ts,tsx} : Use semantic HTML elements (`<label>`, `<output>`) over ARIA attributes when possible; every form control must have a label
Applied to files:
components/user/subscriptions/UserPageSubscriptions.module.scss
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: Fix with modernization (no `// eslint-disable` unless explicitly instructed); prefer refactors aligned with React 19.2, React Compiler, and Next.js 16 conventions
Applied to files:
components/user/subscriptions/UserPageSubscriptions.module.scsscomponents/user/subscriptions/UserPageSubscriptionsTopUp.tsx
📚 Learning: 2025-11-25T08:35:58.729Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T08:35:58.729Z
Learning: Applies to **/*.{tsx,jsx} : Use FontAwesome for icons in React components
Applied to files:
components/user/subscriptions/UserPageSubscriptionsTopUp.tsx
🧬 Code graph analysis (1)
components/user/subscriptions/UserPageSubscriptionsTopUp.tsx (4)
constants.ts (4)
MEMES_MINT_PRICE(74-74)SUBSCRIPTIONS_CHAIN(61-61)SUBSCRIPTIONS_ADDRESS_ENS(67-67)SUBSCRIPTIONS_ADDRESS(65-66)components/distribution-plan-tool/common/CircleLoader.tsx (1)
CircleLoader(9-39)helpers/Helpers.ts (3)
getTransactionLink(265-274)formatAddress(32-47)numberWithCommasFromString(73-82)components/show-more-button/ShowMoreButton.tsx (1)
ShowMoreButton(7-35)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (4)
components/user/subscriptions/UserPageSubscriptions.module.scss (1)
115-150: Accessibility improvements look good.The custom radio input styling properly implements
:focus-visiblefor keyboard accessibility,appearance: nonefor cross-browser consistency, and a clear visual checked state. This addresses previous concerns about focus indicators.components/user/subscriptions/UserPageSubscriptionsTopUp.tsx (3)
44-46: LGTM!The
getEthForCardshelper correctly handles floating-point precision by rounding to avoid arithmetic issues with JavaScript decimals.
91-93: LGTM!The
mountedstate pattern correctly prevents hydration mismatches when usingcreatePortalin a Next.js environment.
265-279: Consider guardingwindow.location.hreffor SSR safety.Although this code path is gated by
isIos, theiOsContentJSX withwindow.location.hrefis evaluated during render regardless of whether it's displayed. This could cause issues during SSR or static generation.Consider using a state-based approach or conditional evaluation:
🔎 Suggested fix
const iOsContent = ( <Row className="pt-2"> <Col> - <Link - href={window.location.href} + <a + href={typeof window !== "undefined" ? window.location.href : "#"} className="text-center pt-2 pb-2" target="_blank" rel="noopener noreferrer"> <button className="btn btn-light" style={{ width: "100%" }}> Top-up on 6529.io </button> - </Link> + </a> </Col> </Row> );Note: Using
<a>instead of<Link>is appropriate here sincetarget="_blank"opens in a new tab (external navigation behavior).
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx (1)
89-89: Type mismatch:dateprop can benullbut type doesn't reflect it.Line 89 passes
rows[index] ?? null, but line 114 declaresdate: SeasonMintRow(non-nullable). This will cause TypeScript errors and potential runtime crashes whendateisnullsince lines 267 and 289 accessprops.date.seasonIndexandprops.date.utcDaywithout null checks.🔎 Apply this diff to fix the type and add null guards:
function SubscriptionRow( props: Readonly<{ profileKey: string; title: string; subscription: NFTSubscription; eligibilityCount: number; readonly: boolean; minting_today?: boolean; first: boolean; - date: SeasonMintRow; + date: SeasonMintRow | null; refresh: () => void; }> ) {Then guard the usages in the JSX:
- <span> - - SZN{displayedSeasonNumberFromIndex(props.date.seasonIndex)} - </span> + {props.date && ( + <span> + - SZN{displayedSeasonNumberFromIndex(props.date.seasonIndex)} + </span> + )}) : ( - <span>{formatFullDate(props.date.utcDay)}</span> + <span>{props.date ? formatFullDate(props.date.utcDay) : "TBD"}</span> )}Also applies to: 114-114
🧹 Nitpick comments (1)
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx (1)
196-202: Error handling improved, but error message extraction could be more robust.The redundant
setSubscribedcall from the past review has been correctly removed. However,typeof e === "string"rarely matches API errors which are typically Error objects.🔎 Consider this more robust error extraction:
} catch (e: unknown) { setToast({ message: - typeof e === "string" ? e : "Failed to change token subscription.", + e instanceof Error ? e.message : "Failed to change token subscription.", type: "error", }); return;
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx(7 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx,js,jsx}: Do not include any comments in the code; it should be self-explanatory
Write correct, up-to-date, bug-free, fully componentized, secure, and efficient code
Include all required imports and ensure proper naming of key components
Use NextJS features that match the current version
**/*.{ts,tsx,js,jsx}: Replace<img>elements with<Image />fromnext/imageto satisfy@next/next/no-img-elementESLint rule
Use<Link href="/path">from Next.js for internal navigation instead of plain HTML links to satisfy@next/next/no-html-link-for-pagesESLint rule
Files:
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx
**/*.{tsx,jsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{tsx,jsx}: Use FontAwesome for icons in React components
Use TailwindCSS for styling in React components
Use react-query for data fetching
Always addreadonlybefore props in React components
Files:
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks)
Use framework APIs: internal links should use<Link>, images should usenext/image, and adopt Next's ESLint rules (Core Web Vitals)
**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks rules)
Follow existing code style and naming conventions; maintain clean code standards (measured by SonarQube)
Files:
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Must passtsc --noEmittype checking
Prefer direct named imports for React hooks and types (import { useMemo, useRef, FC, etc. } from "react") overReact.namespace usage (React.useMemo,React.useRef, etc.)
If thereact-hooks/exhaustive-depslint rule is triggered: if the Effect only derives state, remove the Effect and compute during render; if listening to an external system and needing fresh props/state, wrap non-reactive logic inuseEffectEvent
**/*.{ts,tsx}: Must passtsc --noEmitfor TypeScript type checking
Prefer Server Components over Client Components; use Server Functions/Server Actions ('use server') for mutations
Remove unnecessary Effects; if Effect only derives state, compute during render instead
UseuseEffectEventfor non-reactive logic inside Effects to avoid unnecessary re-runs
Use framework APIs:<Link>for internal links,next/imagefor images, adopt Next's ESLint rules
Use'use cache'directive and Cache Components features for explicit opt-in caching in Next.js 16
Use TypeScript and React functional components with hooks
When parsing Seize URLs or similar, fail fast if base origin is unavailable; do not fall back to placeholder origins
Replace<img>elements with<Image />fromnext/image
Use<Link href="/path">for internal navigation instead of plain HTML links
Move data fetches to Server Components; handle mutations through Server Functions/Server Actions with'use server'directive
Files:
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx
🧠 Learnings (1)
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: Fix with modernization (no `// eslint-disable` unless explicitly instructed); prefer refactors aligned with React 19.2, React Compiler, and Next.js 16 conventions
Applied to files:
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (4)
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx (4)
1-27: LGTM!Imports are appropriate and organized. The new
formatAddressimport supports the address formatting feature added in the component.
137-141: Good implementation of the eligibility count guard.This
useEffectproperly addresses the edge case whereeligibilityCountdecreases belowselectedCount, preventing invalid UI states. The conditional ensures no infinite loop.
208-255: LGTM!The
handleUpdateSubscriptionCountfunction has proper error handling with state revert on failure. TheisSubmittingguard prevents race conditions from rapid interactions.
315-344: Count selector implementation looks good.The selector properly:
- Disables when
eligibilityCount <= 1(line 319)- Generates options from 1 to
eligibilityCount(lines 330-337)- Uses optimistic updates with proper revert on error
One minor consideration: rapid changes trigger multiple API calls. Consider debouncing if this becomes a UX issue.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx (1)
114-114: Fix type mismatch:dateprop should be nullable.Line 89 passes
rows[index] ?? null, but the prop type at line 114 declaresdate: SeasonMintRow(non-nullable). This creates a TypeScript type error and will cause runtime crashes when accessingprops.date.seasonIndex(line 267) orprops.date.utcDay(line 289) without null guards.🔎 Update the prop type to allow null:
props: Readonly<{ profileKey: string; title: string; subscription: NFTSubscription; eligibilityCount: number; readonly: boolean; minting_today?: boolean; first: boolean; - date: SeasonMintRow; + date: SeasonMintRow | null; refresh: () => void; }>
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx(7 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx,js,jsx}: Do not include any comments in the code; it should be self-explanatory
Write correct, up-to-date, bug-free, fully componentized, secure, and efficient code
Include all required imports and ensure proper naming of key components
Use NextJS features that match the current version
**/*.{ts,tsx,js,jsx}: Replace<img>elements with<Image />fromnext/imageto satisfy@next/next/no-img-elementESLint rule
Use<Link href="/path">from Next.js for internal navigation instead of plain HTML links to satisfy@next/next/no-html-link-for-pagesESLint rule
Files:
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx
**/*.{tsx,jsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{tsx,jsx}: Use FontAwesome for icons in React components
Use TailwindCSS for styling in React components
Use react-query for data fetching
Always addreadonlybefore props in React components
Files:
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks)
Use framework APIs: internal links should use<Link>, images should usenext/image, and adopt Next's ESLint rules (Core Web Vitals)
**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks rules)
Follow existing code style and naming conventions; maintain clean code standards (measured by SonarQube)
Files:
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Must passtsc --noEmittype checking
Prefer direct named imports for React hooks and types (import { useMemo, useRef, FC, etc. } from "react") overReact.namespace usage (React.useMemo,React.useRef, etc.)
If thereact-hooks/exhaustive-depslint rule is triggered: if the Effect only derives state, remove the Effect and compute during render; if listening to an external system and needing fresh props/state, wrap non-reactive logic inuseEffectEvent
**/*.{ts,tsx}: Must passtsc --noEmitfor TypeScript type checking
Prefer Server Components over Client Components; use Server Functions/Server Actions ('use server') for mutations
Remove unnecessary Effects; if Effect only derives state, compute during render instead
UseuseEffectEventfor non-reactive logic inside Effects to avoid unnecessary re-runs
Use framework APIs:<Link>for internal links,next/imagefor images, adopt Next's ESLint rules
Use'use cache'directive and Cache Components features for explicit opt-in caching in Next.js 16
Use TypeScript and React functional components with hooks
When parsing Seize URLs or similar, fail fast if base origin is unavailable; do not fall back to placeholder origins
Replace<img>elements with<Image />fromnext/image
Use<Link href="/path">for internal navigation instead of plain HTML links
Move data fetches to Server Components; handle mutations through Server Functions/Server Actions with'use server'directive
Files:
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx
🧠 Learnings (2)
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: Fix with modernization (no `// eslint-disable` unless explicitly instructed); prefer refactors aligned with React 19.2, React Compiler, and Next.js 16 conventions
Applied to files:
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/{components,contexts,hooks}/**/*.{ts,tsx} : Use semantic HTML elements (`<label>`, `<output>`) over ARIA attributes when possible; every form control must have a label
Applied to files:
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (javascript-typescript)
Signed-off-by: prxt6529 <prxt@6529.io>
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx (1)
123-123: Consider explicit boolean conversion for clarity.The
!!operator works correctly but is less explicit than alternatives. Per coding guidelines preferring explicit logic, consider usingBoolean()for clearer intent.🔎 Apply this diff for more explicit boolean conversion:
- const [subscribed, setSubscribed] = useState<boolean>( - !!props.subscription.subscribed - ); + const [subscribed, setSubscribed] = useState<boolean>( + Boolean(props.subscription.subscribed) + );Based on learnings from coding guidelines that recommend avoiding double negatives and preferring explicit logic.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx(1 hunks)components/user/subscriptions/UserPageSubscriptionsTopUp.tsx(6 hunks)components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx(7 hunks)
🧰 Additional context used
📓 Path-based instructions (11)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx,js,jsx}: Do not include any comments in the code; it should be self-explanatory
Write correct, up-to-date, bug-free, fully componentized, secure, and efficient code
Include all required imports and ensure proper naming of key components
Use NextJS features that match the current version
**/*.{ts,tsx,js,jsx}: Replace<img>elements with<Image />fromnext/imageto satisfy@next/next/no-img-elementESLint rule
Use<Link href="/path">from Next.js for internal navigation instead of plain HTML links to satisfy@next/next/no-html-link-for-pagesESLint rule
Files:
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsxcomponents/user/subscriptions/UserPageSubscriptionsTopUp.tsx
**/*.{tsx,jsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{tsx,jsx}: Use FontAwesome for icons in React components
Use TailwindCSS for styling in React components
Use react-query for data fetching
Always addreadonlybefore props in React components
Files:
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsxcomponents/user/subscriptions/UserPageSubscriptionsTopUp.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks)
Use framework APIs: internal links should use<Link>, images should usenext/image, and adopt Next's ESLint rules (Core Web Vitals)
**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks rules)
Follow existing code style and naming conventions; maintain clean code standards (measured by SonarQube)
Files:
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsxcomponents/user/subscriptions/UserPageSubscriptionsTopUp.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Must passtsc --noEmittype checking
Prefer direct named imports for React hooks and types (import { useMemo, useRef, FC, etc. } from "react") overReact.namespace usage (React.useMemo,React.useRef, etc.)
If thereact-hooks/exhaustive-depslint rule is triggered: if the Effect only derives state, remove the Effect and compute during render; if listening to an external system and needing fresh props/state, wrap non-reactive logic inuseEffectEvent
**/*.{ts,tsx}: Must passtsc --noEmitfor TypeScript type checking
Prefer Server Components over Client Components; use Server Functions/Server Actions ('use server') for mutations
Remove unnecessary Effects; if Effect only derives state, compute during render instead
UseuseEffectEventfor non-reactive logic inside Effects to avoid unnecessary re-runs
Use framework APIs:<Link>for internal links,next/imagefor images, adopt Next's ESLint rules
Use'use cache'directive and Cache Components features for explicit opt-in caching in Next.js 16
Use TypeScript and React functional components with hooks
When parsing Seize URLs or similar, fail fast if base origin is unavailable; do not fall back to placeholder origins
Replace<img>elements with<Image />fromnext/image
Use<Link href="/path">for internal navigation instead of plain HTML links
Move data fetches to Server Components; handle mutations through Server Functions/Server Actions with'use server'directive
Files:
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsxcomponents/user/subscriptions/UserPageSubscriptionsTopUp.tsx
**/@(__tests__|*.test).{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Tests should live in
__tests__/orComponentName.test.tsx; mock external dependencies and APIs in tests
Files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx
**/__tests__/**/*.{ts,tsx}
📄 CodeRabbit inference engine (GEMINI.md)
Place tests in
__tests__/directory or asComponentName.test.tsxalongside components
Files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx
**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (GEMINI.md)
Mock external dependencies and APIs in tests
Files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx
__tests__/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (tests/AGENTS.md)
Use Jest +
ts-jestfor TypeScript testing
Files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx
__tests__/**/*.{ts,tsx}
📄 CodeRabbit inference engine (tests/AGENTS.md)
Functions must have ≤ 15 cognitive complexity; extract deep ternaries (>3 levels) and break down complex logic
Files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx
__tests__/**/*.{ts,tsx,js}
📄 CodeRabbit inference engine (tests/AGENTS.md)
__tests__/**/*.{ts,tsx,js}: Preferfor...ofloops overforEachas it allowsbreak/continueand works with async/await
Usearray.at(-1)andarray.at(-2)instead of index-based array access for negative indexing
UseString.prototype.replaceAll()instead ofreplace()for global string replacements
UseglobalThis.fetch()instead of directfetch()calls
Organize imports with one import per module in order: external → internal → types, with no duplicates
Useelement.remove()instead ofparent.removeChild(element)for DOM manipulation
Catch errors only when meaningful; no empty catch blocks; log errors with context
Avoid double negatives in code; prefer explicit logic and remove redundant annotations; use optional chaining (?.)
Files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx
__tests__/**/{components,contexts,hooks}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (tests/AGENTS.md)
Use semantic HTML elements (
<label>,<output>) over ARIA attributes when possible; every form control must have a label
Files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx
🧠 Learnings (8)
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: Fix with modernization (no `// eslint-disable` unless explicitly instructed); prefer refactors aligned with React 19.2, React Compiler, and Next.js 16 conventions
Applied to files:
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsxcomponents/user/subscriptions/UserPageSubscriptionsTopUp.tsx
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/{components,contexts,hooks}/**/*.{ts,tsx} : Use semantic HTML elements (`<label>`, `<output>`) over ARIA attributes when possible; every form control must have a label
Applied to files:
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/*.{ts,tsx,js} : Avoid double negatives in code; prefer explicit logic and remove redundant annotations; use optional chaining (`?.`)
Applied to files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/__tests__/**/components/**/*.test.{ts,tsx} : Use testing-library/react + user-event for React component tests
Applied to files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/*.{ts,tsx,js} : Use `String.prototype.replaceAll()` instead of `replace()` for global string replacements
Applied to files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: Applies to **/@(__tests__|*.test).{ts,tsx} : Tests should live in `__tests__/` or `ComponentName.test.tsx`; mock external dependencies and APIs in tests
Applied to files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/__tests__/**/components/**/*.test.{ts,tsx} : Test accessibility with keyboard navigation and screen reader compatibility
Applied to files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx
📚 Learning: 2025-11-25T08:35:58.729Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T08:35:58.729Z
Learning: Applies to **/*.{tsx,jsx} : Use FontAwesome for icons in React components
Applied to files:
components/user/subscriptions/UserPageSubscriptionsTopUp.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (13)
components/user/subscriptions/UserPageSubscriptionsTopUp.tsx (8)
44-46: LGTM!The
getEthForCardshelper uses appropriate floating-point rounding to avoid precision issues when calculating ETH values.
80-93: LGTM!The
mountedstate for portal hydration safety anduseReffor input focus (addressing the previous querySelector concern) are implemented correctly.
95-135: LGTM!The
handleSendfunction properly validates the selected option, maps option IDs to card counts, and correctly calculates the ETH value usingparseEther.
153-187: LGTM!Body scroll locking, escape key handling, and modal close logic are properly implemented with appropriate cleanup in effect returns.
189-259: LGTM!The
getModalContentfunction properly handles all transaction states (error, pending, loading, success) with appropriate UI feedback and external transaction links.
368-444: LGTM!The "Other" option implementation with
useReffor input focus and proper event propagation handling is well done. The Send button's disabled logic correctly validates all edge cases.
532-574: LGTM!The
CardCountOptioncomponent is well-structured withreadonlyprops, proper accessibility attributes, and clean selection handling.
477-527: LGTM!The modal portal implementation with
mountedguard for SSR safety,AnimatePresencefor animations, and conditional close behavior based onisClosableis well-designed.__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx (1)
209-209: LGTM! Regex matcher improves test robustness.The change from an exact string match to a regex pattern makes the test more flexible and better accommodates variations in whitespace or surrounding text while still verifying the essential "Minting Today" message appears.
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx (4)
18-18: LGTM! Appropriate helper import for address formatting.The
formatAddressimport supports the new phase information display at line 302, where it formats the airdrop address for better readability.
89-89: Excellent work addressing all previous review concerns!All five issues flagged in the previous review have been properly resolved:
- Line 196-206: Removed redundant
setSubscribed(subscribed)call from catch block ✓- Lines 137-141: Added
useEffectto clampselectedCountwheneligibilityCountdecreases ✓- Lines 89, 114, 266-295: Added null handling for
rows[index]with proper type guards and conditional rendering ✓- Line 335: Added
aria-labelto the subscription count selector ✓- Lines 266-295: Added comprehensive null guards for
props.dateaccess ✓The implementations are clean, correct, and follow React best practices.
Also applies to: 114-114, 137-141, 196-206, 266-295, 335-335
126-141: LGTM! Well-implemented subscription count feature with optimistic UI.The new subscription count selector demonstrates solid patterns:
- State synchronization (lines 126-141):
useEffecthooks properly syncselectedCountwith external changes and clamp toeligibilityCount- Optimistic updates (lines 329-333): UI updates immediately while API call proceeds in background
- Error recovery (line 245): Reverts to
subscribedCounton failure with clear error toast- Race condition protection (line 211):
isSubmittingguard prevents concurrent API calls- Accessibility (line 335): Proper
aria-labelfor screen readersThe
handleUpdateSubscriptionCountfunction correctly mirrors the structure of the existingsubmitfunction and integrates cleanly with the component's state management.Also applies to: 208-255, 317-350
65-68: LGTM! Layout refactor improves maintainability and visual hierarchy.The transition from table-based to flex-based layout brings several benefits:
- Clearer structure (lines 65-68): Semibold header with horizontal rule provides strong visual separation
- Flexible rendering (lines 71-93): Dynamic odd/even/last styling via CSS modules enables better theming
- Responsive design (lines 260-306): Two-column layout with proper gap spacing adapts well to different screen sizes
- Conditional information (lines 297-305): Phase details display only when available and for the first subscription, reducing clutter
The refactor maintains all existing functionality while improving code maintainability and visual presentation.
Also applies to: 71-99, 260-306
Signed-off-by: prxt6529 <prxt@6529.io>
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
components/user/subscriptions/UserPageSubscriptionsHistory.tsx (1)
263-269: Consider removing unnecessary type assertion.The
as Dateassertion on line 267 is misleading since the coalesced timestamp value is likely a number or string, not a Date object. Thenew Date()constructor handles number/string timestamps correctly, so the assertion adds no value:{(props.redeem.transaction_date ?? props.redeem.created_at) && getDateDisplay( new Date( - (props.redeem.transaction_date ?? - props.redeem.created_at) as Date + props.redeem.transaction_date ?? props.redeem.created_at ) )}
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (33)
generated/models/AirdropAddressResponse.tsis excluded by!**/generated/**generated/models/AirdropAddressResponseTdhWallet.tsis excluded by!**/generated/**generated/models/AllowlistNormalizedEntry.tsis excluded by!**/generated/**generated/models/ApiUpdateWaveRequest.tsis excluded by!**/generated/**generated/models/DistributionNormalized.tsis excluded by!**/generated/**generated/models/DistributionNormalizedPage.tsis excluded by!**/generated/**generated/models/DistributionPhasesPage.tsis excluded by!**/generated/**generated/models/DistributionPhoto.tsis excluded by!**/generated/**generated/models/DistributionPhotosPage.tsis excluded by!**/generated/**generated/models/GetRedeemedMemeSubscriptionCounts200Response.tsis excluded by!**/generated/**generated/models/NFTFinalSubscription.tsis excluded by!**/generated/**generated/models/NFTFinalSubscriptionUpload.tsis excluded by!**/generated/**generated/models/NFTFinalSubscriptionUploadPage.tsis excluded by!**/generated/**generated/models/NFTSubscription.tsis excluded by!**/generated/**generated/models/ObjectSerializer.tsis excluded by!**/generated/**generated/models/RedeemedSubscription.tsis excluded by!**/generated/**generated/models/RedeemedSubscriptionCounts.tsis excluded by!**/generated/**generated/models/RedeemedSubscriptionCountsPage.tsis excluded by!**/generated/**generated/models/RedeemedSubscriptionPage.tsis excluded by!**/generated/**generated/models/SubscribeAllEditionsResponse.tsis excluded by!**/generated/**generated/models/SubscriptionCountResponse.tsis excluded by!**/generated/**generated/models/SubscriptionCounts.tsis excluded by!**/generated/**generated/models/SubscriptionDetails.tsis excluded by!**/generated/**generated/models/SubscriptionLog.tsis excluded by!**/generated/**generated/models/SubscriptionLogPage.tsis excluded by!**/generated/**generated/models/SubscriptionModeResponse.tsis excluded by!**/generated/**generated/models/SubscriptionResponse.tsis excluded by!**/generated/**generated/models/SubscriptionTopUp.tsis excluded by!**/generated/**generated/models/SubscriptionTopUpPage.tsis excluded by!**/generated/**generated/models/UpdateSubscribeAllEditionsRequest.tsis excluded by!**/generated/**generated/models/UpdateSubscriptionCountRequest.tsis excluded by!**/generated/**generated/models/UpdateSubscriptionModeRequest.tsis excluded by!**/generated/**generated/models/UpdateSubscriptionRequest.tsis excluded by!**/generated/**
📒 Files selected for processing (10)
__tests__/components/user/subscriptions/UserPageSubscriptionsMode.test.tsx(1 hunks)__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx(3 hunks)components/subscriptions-report/SubscriptionsReport.tsx(1 hunks)components/user/subscriptions/UserPageSubscriptions.tsx(5 hunks)components/user/subscriptions/UserPageSubscriptionsBalance.tsx(1 hunks)components/user/subscriptions/UserPageSubscriptionsEditionPreference.tsx(1 hunks)components/user/subscriptions/UserPageSubscriptionsHistory.tsx(5 hunks)components/user/subscriptions/UserPageSubscriptionsMode.tsx(3 hunks)components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx(7 hunks)entities/ISubscription.ts(0 hunks)
💤 Files with no reviewable changes (1)
- entities/ISubscription.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- components/user/subscriptions/UserPageSubscriptions.tsx
🧰 Additional context used
📓 Path-based instructions (11)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx,js,jsx}: Do not include any comments in the code; it should be self-explanatory
Write correct, up-to-date, bug-free, fully componentized, secure, and efficient code
Include all required imports and ensure proper naming of key components
Use NextJS features that match the current version
**/*.{ts,tsx,js,jsx}: Replace<img>elements with<Image />fromnext/imageto satisfy@next/next/no-img-elementESLint rule
Use<Link href="/path">from Next.js for internal navigation instead of plain HTML links to satisfy@next/next/no-html-link-for-pagesESLint rule
Files:
components/subscriptions-report/SubscriptionsReport.tsxcomponents/user/subscriptions/UserPageSubscriptionsEditionPreference.tsxcomponents/user/subscriptions/UserPageSubscriptionsBalance.tsxcomponents/user/subscriptions/UserPageSubscriptionsUpcoming.tsxcomponents/user/subscriptions/UserPageSubscriptionsHistory.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsMode.test.tsxcomponents/user/subscriptions/UserPageSubscriptionsMode.tsx
**/*.{tsx,jsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{tsx,jsx}: Use FontAwesome for icons in React components
Use TailwindCSS for styling in React components
Use react-query for data fetching
Always addreadonlybefore props in React components
Files:
components/subscriptions-report/SubscriptionsReport.tsxcomponents/user/subscriptions/UserPageSubscriptionsEditionPreference.tsxcomponents/user/subscriptions/UserPageSubscriptionsBalance.tsxcomponents/user/subscriptions/UserPageSubscriptionsUpcoming.tsxcomponents/user/subscriptions/UserPageSubscriptionsHistory.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsMode.test.tsxcomponents/user/subscriptions/UserPageSubscriptionsMode.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks)
Use framework APIs: internal links should use<Link>, images should usenext/image, and adopt Next's ESLint rules (Core Web Vitals)
**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks rules)
Follow existing code style and naming conventions; maintain clean code standards (measured by SonarQube)
Files:
components/subscriptions-report/SubscriptionsReport.tsxcomponents/user/subscriptions/UserPageSubscriptionsEditionPreference.tsxcomponents/user/subscriptions/UserPageSubscriptionsBalance.tsxcomponents/user/subscriptions/UserPageSubscriptionsUpcoming.tsxcomponents/user/subscriptions/UserPageSubscriptionsHistory.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsMode.test.tsxcomponents/user/subscriptions/UserPageSubscriptionsMode.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Must passtsc --noEmittype checking
Prefer direct named imports for React hooks and types (import { useMemo, useRef, FC, etc. } from "react") overReact.namespace usage (React.useMemo,React.useRef, etc.)
If thereact-hooks/exhaustive-depslint rule is triggered: if the Effect only derives state, remove the Effect and compute during render; if listening to an external system and needing fresh props/state, wrap non-reactive logic inuseEffectEvent
**/*.{ts,tsx}: Must passtsc --noEmitfor TypeScript type checking
Prefer Server Components over Client Components; use Server Functions/Server Actions ('use server') for mutations
Remove unnecessary Effects; if Effect only derives state, compute during render instead
UseuseEffectEventfor non-reactive logic inside Effects to avoid unnecessary re-runs
Use framework APIs:<Link>for internal links,next/imagefor images, adopt Next's ESLint rules
Use'use cache'directive and Cache Components features for explicit opt-in caching in Next.js 16
Use TypeScript and React functional components with hooks
When parsing Seize URLs or similar, fail fast if base origin is unavailable; do not fall back to placeholder origins
Replace<img>elements with<Image />fromnext/image
Use<Link href="/path">for internal navigation instead of plain HTML links
Move data fetches to Server Components; handle mutations through Server Functions/Server Actions with'use server'directive
Files:
components/subscriptions-report/SubscriptionsReport.tsxcomponents/user/subscriptions/UserPageSubscriptionsEditionPreference.tsxcomponents/user/subscriptions/UserPageSubscriptionsBalance.tsxcomponents/user/subscriptions/UserPageSubscriptionsUpcoming.tsxcomponents/user/subscriptions/UserPageSubscriptionsHistory.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsMode.test.tsxcomponents/user/subscriptions/UserPageSubscriptionsMode.tsx
**/@(__tests__|*.test).{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Tests should live in
__tests__/orComponentName.test.tsx; mock external dependencies and APIs in tests
Files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsMode.test.tsx
**/__tests__/**/*.{ts,tsx}
📄 CodeRabbit inference engine (GEMINI.md)
Place tests in
__tests__/directory or asComponentName.test.tsxalongside components
Files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsMode.test.tsx
**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (GEMINI.md)
Mock external dependencies and APIs in tests
Files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsMode.test.tsx
__tests__/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (tests/AGENTS.md)
Use Jest +
ts-jestfor TypeScript testing
Files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsMode.test.tsx
__tests__/**/*.{ts,tsx}
📄 CodeRabbit inference engine (tests/AGENTS.md)
Functions must have ≤ 15 cognitive complexity; extract deep ternaries (>3 levels) and break down complex logic
Files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsMode.test.tsx
__tests__/**/*.{ts,tsx,js}
📄 CodeRabbit inference engine (tests/AGENTS.md)
__tests__/**/*.{ts,tsx,js}: Preferfor...ofloops overforEachas it allowsbreak/continueand works with async/await
Usearray.at(-1)andarray.at(-2)instead of index-based array access for negative indexing
UseString.prototype.replaceAll()instead ofreplace()for global string replacements
UseglobalThis.fetch()instead of directfetch()calls
Organize imports with one import per module in order: external → internal → types, with no duplicates
Useelement.remove()instead ofparent.removeChild(element)for DOM manipulation
Catch errors only when meaningful; no empty catch blocks; log errors with context
Avoid double negatives in code; prefer explicit logic and remove redundant annotations; use optional chaining (?.)
Files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsMode.test.tsx
__tests__/**/{components,contexts,hooks}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (tests/AGENTS.md)
Use semantic HTML elements (
<label>,<output>) over ARIA attributes when possible; every form control must have a label
Files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsMode.test.tsx
🧠 Learnings (8)
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: Fix with modernization (no `// eslint-disable` unless explicitly instructed); prefer refactors aligned with React 19.2, React Compiler, and Next.js 16 conventions
Applied to files:
components/user/subscriptions/UserPageSubscriptionsEditionPreference.tsxcomponents/user/subscriptions/UserPageSubscriptionsUpcoming.tsx
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/*.{ts,tsx,js} : Avoid double negatives in code; prefer explicit logic and remove redundant annotations; use optional chaining (`?.`)
Applied to files:
components/user/subscriptions/UserPageSubscriptionsEditionPreference.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsMode.test.tsx
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/{components,contexts,hooks}/**/*.{ts,tsx} : Use semantic HTML elements (`<label>`, `<output>`) over ARIA attributes when possible; every form control must have a label
Applied to files:
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: Applies to **/@(__tests__|*.test).{ts,tsx} : Tests should live in `__tests__/` or `ComponentName.test.tsx`; mock external dependencies and APIs in tests
Applied to files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx
📚 Learning: 2025-12-05T10:55:30.871Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-05T10:55:30.871Z
Learning: Applies to **/*.test.{ts,tsx} : Mock external dependencies and APIs in tests
Applied to files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/__tests__/**/components/**/*.test.{ts,tsx} : Use testing-library/react + user-event for React component tests
Applied to files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/__tests__/**/components/**/*.test.{ts,tsx} : Test accessibility with keyboard navigation and screen reader compatibility
Applied to files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx
📚 Learning: 2025-12-05T10:55:30.871Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-05T10:55:30.871Z
Learning: Applies to **/*.{ts,tsx} : Use TypeScript and React functional components with hooks
Applied to files:
components/user/subscriptions/UserPageSubscriptionsMode.tsx
🧬 Code graph analysis (3)
components/user/subscriptions/UserPageSubscriptionsEditionPreference.tsx (3)
generated/models/SubscriptionDetails.ts (1)
SubscriptionDetails(15-71)components/auth/Auth.tsx (1)
AuthContext(83-93)components/distribution-plan-tool/common/CircleLoader.tsx (1)
CircleLoader(9-39)
components/user/subscriptions/UserPageSubscriptionsHistory.tsx (1)
helpers/Helpers.ts (3)
getDateDisplay(167-187)formatAddress(32-47)getTransactionLink(265-274)
components/user/subscriptions/UserPageSubscriptionsMode.tsx (1)
components/distribution-plan-tool/common/CircleLoader.tsx (1)
CircleLoader(9-39)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (19)
components/user/subscriptions/UserPageSubscriptionsMode.tsx (3)
84-96: LGTM! Clear timestamp display with proper UTC formatting.The conditional rendering and date formatting are well-implemented. The explicit UTC timezone specification ensures consistent display regardless of the user's local timezone.
4-6: LGTM! CircleLoader integration follows established patterns.The replacement of Spinner with CircleLoader is consistent with the broader UI updates across subscription components, and the size specification aligns with the component's API.
Also applies to: 116-116
80-127: LGTM! Layout updates maintain accessibility and align with design system.The mixed Bootstrap/Tailwind approach is consistent with the broader refactoring. The terminology shift to "drops" and proper label associations maintain clarity and accessibility.
components/user/subscriptions/UserPageSubscriptionsEditionPreference.tsx (4)
34-77: LGTM! Error handling and async flow properly implemented.The toggle logic correctly addresses all concerns from previous reviews:
requestAuthis called within the try block, ensuring cleanup happens even if it rejects- Error messages are safely extracted with proper fallbacks
- State cleanup is handled exclusively in the finally block, avoiding redundancy
27-28: LGTM! Eligibility count display with sensible default.The fallback to 1 for eligibility count is appropriate, and the display format clearly communicates the multiplier to users.
Also applies to: 86-86
30-32: LGTM! Effect properly scoped to specific prop.The dependency array correctly tracks only the specific nested property being synced, avoiding unnecessary re-runs.
93-109: LGTM! Proper label associations for accessibility.The toggle control is correctly associated with its labels via matching
htmlForandidattributes, meeting accessibility requirements.__tests__/components/user/subscriptions/UserPageSubscriptionsMode.test.tsx (1)
27-28: LGTM! Test data updated to match generated model.The addition of
subscribe_all_editionsandsubscription_eligibility_countaligns the test fixture with the expandedSubscriptionDetailsmodel structure.components/user/subscriptions/UserPageSubscriptionsHistory.tsx (2)
230-231: LGTM! Proper null-safe date rendering.The optional chaining correctly guards against missing timestamps before attempting to format them.
253-254: LGTM! Clear quantity display format.The "x{count}" format effectively communicates the edition quantity received.
components/user/subscriptions/UserPageSubscriptionsBalance.tsx (1)
4-4: LGTM! Import updated to generated model.This change aligns with the broader migration from local interfaces to generated models across subscription components.
components/subscriptions-report/SubscriptionsReport.tsx (1)
18-19: LGTM! Imports updated to generated models.The migration to generated models for
RedeemedSubscriptionCountsandSubscriptionCountsaligns with the broader refactoring across subscription-related components.__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx (2)
83-90: LGTM! Test fixture updated to match generated model.The mock data structure correctly reflects all fields in the
SubscriptionDetailsmodel, with appropriate test values.
215-215: LGTM! Regex match improves test resilience.Using a regex pattern instead of an exact string match makes the test more robust to text variations while still validating the key message.
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx (5)
80-87: Past issue resolved: Undefinedrows[index]now handled correctly.The previous concern about
rows[index]potentially being undefined when there are fewer eligible mint days than subscriptions has been addressed. Line 87 now usesdate={rows[index] ?? null}to safely handle this case, and the prop type at line 112 correctly declaresdate: SeasonMintRow | null.
124-139: Past issue resolved: Validation added wheneligibilityCountdecreases.The previous concern about the count selector lacking validation when
eligibilityCountdecreases belowselectedCounthas been properly addressed. The useEffect at lines 135-139 now watches for changes toeligibilityCountand automatically clampsselectedCountdown to the new limit (with a minimum of 1), ensuring the select element always has a valid matching option.
194-203: Past issue resolved: Redundant state setter removed from error handler.The previous redundant
setSubscribed(subscribed)call in the catch block has been correctly removed. The error handler now only displays the toast message and returns, which is the appropriate behavior since thesubscribedstate was never mutated during the failed async operation.
264-293: Past issue resolved: Null guards properly added forprops.dateaccess.The previous concern about unguarded access to
props.date.seasonIndexandprops.date.utcDayhas been correctly addressed. Line 264 now wraps the entire date/season display block in a conditional{props.date && ...}, ensuring no property access occurs whendateis null. This prevents runtime errors when there are fewer eligible mint days than subscriptions.
318-352: Past issue resolved: Accessible label added to count selector.The previous concern about the missing
aria-labelon the count selector has been addressed. Line 336 now includesaria-label="Select subscription quantity for ${props.title}", making the control's purpose clear to screen reader users. The implementation properly disables the selector wheneligibilityCount <= 1or during submission, and the options are correctly generated from the eligibility count.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
components/user/layout/UserPageLayout.tsx (1)
17-18: Consider removing unnecessary optional chaining.The
initialProfileparameter is typed as non-nullableApiIdentity, so the optional chaining operator (?.) on line 17 is unnecessary. For consistency with the type system, consider using direct property access:🔎 Suggested refactor
const mainAddress = - initialProfile?.primary_wallet ?? normalizedHandleOrWallet; + initialProfile.primary_wallet ?? normalizedHandleOrWallet;
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
generated/models/GetRedeemedMemeSubscriptionCounts200Response.tsis excluded by!**/generated/**
📒 Files selected for processing (1)
components/user/layout/UserPageLayout.tsx(1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx,js,jsx}: Do not include any comments in the code; it should be self-explanatory
Write correct, up-to-date, bug-free, fully componentized, secure, and efficient code
Include all required imports and ensure proper naming of key components
Use NextJS features that match the current version
**/*.{ts,tsx,js,jsx}: Replace<img>elements with<Image />fromnext/imageto satisfy@next/next/no-img-elementESLint rule
Use<Link href="/path">from Next.js for internal navigation instead of plain HTML links to satisfy@next/next/no-html-link-for-pagesESLint rule
Files:
components/user/layout/UserPageLayout.tsx
**/*.{tsx,jsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{tsx,jsx}: Use FontAwesome for icons in React components
Use TailwindCSS for styling in React components
Use react-query for data fetching
Always addreadonlybefore props in React components
Files:
components/user/layout/UserPageLayout.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks)
Use framework APIs: internal links should use<Link>, images should usenext/image, and adopt Next's ESLint rules (Core Web Vitals)
**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks rules)
Follow existing code style and naming conventions; maintain clean code standards (measured by SonarQube)
Files:
components/user/layout/UserPageLayout.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Must passtsc --noEmittype checking
Prefer direct named imports for React hooks and types (import { useMemo, useRef, FC, etc. } from "react") overReact.namespace usage (React.useMemo,React.useRef, etc.)
If thereact-hooks/exhaustive-depslint rule is triggered: if the Effect only derives state, remove the Effect and compute during render; if listening to an external system and needing fresh props/state, wrap non-reactive logic inuseEffectEvent
**/*.{ts,tsx}: Must passtsc --noEmitfor TypeScript type checking
Prefer Server Components over Client Components; use Server Functions/Server Actions ('use server') for mutations
Remove unnecessary Effects; if Effect only derives state, compute during render instead
UseuseEffectEventfor non-reactive logic inside Effects to avoid unnecessary re-runs
Use framework APIs:<Link>for internal links,next/imagefor images, adopt Next's ESLint rules
Use'use cache'directive and Cache Components features for explicit opt-in caching in Next.js 16
Use TypeScript and React functional components with hooks
When parsing Seize URLs or similar, fail fast if base origin is unavailable; do not fall back to placeholder origins
Replace<img>elements with<Image />fromnext/image
Use<Link href="/path">for internal navigation instead of plain HTML links
Move data fetches to Server Components; handle mutations through Server Functions/Server Actions with'use server'directive
Files:
components/user/layout/UserPageLayout.tsx
🧬 Code graph analysis (1)
components/user/layout/UserPageLayout.tsx (1)
components/user/layout/UserPageClientHydrator.tsx (1)
UserPageClientHydrator(18-49)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (2)
components/user/layout/UserPageLayout.tsx (2)
21-21: LGTM! Flex layout structure is well-designed.The addition of
tw-flex tw-flex-colto the main container andtw-flex-1to the content area creates a proper flexible column layout that allows the page content to expand appropriately.Also applies to: 26-26
30-30: LGTM! Fallback address prop correctly wired.The
fallbackMainAddressprop provides a sensible fallback mechanism for the header component, using the primary wallet when available or falling back to the normalized handle/wallet.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx (1)
136-140: Optimize the useEffect to avoid unnecessary re-runs.Including
selectedCountin the dependency array causes the effect to run on every selection change, even when no clamping is needed. Use a functional state update to eliminate this dependency:🔎 Optimized implementation
useEffect(() => { - if (selectedCount > props.eligibilityCount) { - setSelectedCount(Math.max(1, props.eligibilityCount)); - } -}, [props.eligibilityCount, selectedCount]); + setSelectedCount(prev => Math.min(prev, Math.max(1, props.eligibilityCount))); +}, [props.eligibilityCount]);This ensures
selectedCountis clamped only wheneligibilityCountchanges, avoiding no-op effect runs during normal user interaction.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
generated/models/ObjectSerializer.tsis excluded by!**/generated/**
📒 Files selected for processing (2)
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx(3 hunks)components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx(6 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- tests/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx,js,jsx}: Do not include any comments in the code; it should be self-explanatory
Write correct, up-to-date, bug-free, fully componentized, secure, and efficient code
Include all required imports and ensure proper naming of key components
Use NextJS features that match the current version
**/*.{ts,tsx,js,jsx}: Replace<img>elements with<Image />fromnext/imageto satisfy@next/next/no-img-elementESLint rule
Use<Link href="/path">from Next.js for internal navigation instead of plain HTML links to satisfy@next/next/no-html-link-for-pagesESLint rule
Files:
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx
**/*.{tsx,jsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{tsx,jsx}: Use FontAwesome for icons in React components
Use TailwindCSS for styling in React components
Use react-query for data fetching
Always addreadonlybefore props in React components
Files:
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks)
Use framework APIs: internal links should use<Link>, images should usenext/image, and adopt Next's ESLint rules (Core Web Vitals)
**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks rules)
Follow existing code style and naming conventions; maintain clean code standards (measured by SonarQube)
Files:
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Must passtsc --noEmittype checking
Prefer direct named imports for React hooks and types (import { useMemo, useRef, FC, etc. } from "react") overReact.namespace usage (React.useMemo,React.useRef, etc.)
If thereact-hooks/exhaustive-depslint rule is triggered: if the Effect only derives state, remove the Effect and compute during render; if listening to an external system and needing fresh props/state, wrap non-reactive logic inuseEffectEvent
**/*.{ts,tsx}: Must passtsc --noEmitfor TypeScript type checking
Prefer Server Components over Client Components; use Server Functions/Server Actions ('use server') for mutations
Remove unnecessary Effects; if Effect only derives state, compute during render instead
UseuseEffectEventfor non-reactive logic inside Effects to avoid unnecessary re-runs
Use framework APIs:<Link>for internal links,next/imagefor images, adopt Next's ESLint rules
Use'use cache'directive and Cache Components features for explicit opt-in caching in Next.js 16
Use TypeScript and React functional components with hooks
When parsing Seize URLs or similar, fail fast if base origin is unavailable; do not fall back to placeholder origins
Replace<img>elements with<Image />fromnext/image
Use<Link href="/path">for internal navigation instead of plain HTML links
Move data fetches to Server Components; handle mutations through Server Functions/Server Actions with'use server'directive
Files:
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx
🧠 Learnings (2)
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: Fix with modernization (no `// eslint-disable` unless explicitly instructed); prefer refactors aligned with React 19.2, React Compiler, and Next.js 16 conventions
Applied to files:
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/{components,contexts,hooks}/**/*.{ts,tsx} : Use semantic HTML elements (`<label>`, `<output>`) over ARIA attributes when possible; every form control must have a label
Applied to files:
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (javascript-typescript)
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (5)
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.toggle.test.tsx (2)
65-74: Verify cache invalidation behavior.The test "toggles subscription and posts update" only verifies the API call but doesn't check whether cache invalidation occurs after the subscription toggle. Since
useQueryClientwas added to the mock, this behavior should be tested.🔎 Add cache invalidation assertion
After capturing
mockInvalidateQueries(see previous comment), add:test("toggles subscription and posts update", async () => { const user = userEvent.setup(); (commonApiPost as jest.Mock).mockResolvedValue({ token_id: 1, subscribed: false, }); const { container } = renderComp(); await user.click(screen.getByTestId("toggle")); expect(commonApiPost).toHaveBeenCalled(); + expect(mockInvalidateQueries).toHaveBeenCalled(); });
41-42: Replaceas anywith proper typing.Using
as anybypasses TypeScript's type checking and may hide incomplete mock data. Import the proper types and provide complete mock objects.🔎 Proposed fix with proper types
+import { NFTSubscription } from "@/generated/models/NFTSubscription"; +import { SubscriptionDetails } from "@/generated/models/SubscriptionDetails"; + -const sub = { token_id: 1, contract: "0x123", subscribed: true } as any; -const details = { profile: "test" } as any; +const sub: NFTSubscription = { + token_id: 1, + contract: "0x123", + subscribed: true, +} as NFTSubscription; + +const details: SubscriptionDetails = { + consolidation_key: "test", + last_update: 1234567890, + balance: 1, + automatic: true, + subscribe_all_editions: false, + subscription_eligibility_count: 1, +} as SubscriptionDetails;__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx (3)
9-10: Unused mock: add assertions or remove.
mockInvalidateQueriesis captured and wired into the mock but never asserted in any test. This suggests incomplete test coverage for cache invalidation behavior or unnecessary mock setup.Either add assertions to verify cache invalidation:
it("invalidates cache after subscription toggle", () => { // ... perform action that should invalidate expect(mockInvalidateQueries).toHaveBeenCalledWith( expect.arrayContaining([expect.stringContaining("subscription")]) ); });Or if cache invalidation is not relevant to these tests, remove the captured mock and use an inline
jest.fn().Also applies to: 13-15
65-86: Type assertions may hide incomplete mock data.Using
as NFTSubscriptiontype assertions bypasses TypeScript's structural type checking. If the generatedNFTSubscriptionmodel requires additional fields, these assertions will silently hide the missing data.Consider satisfies operator (TypeScript 4.9+) or remove the assertion to let TypeScript verify completeness:
const mockSubscriptions: NFTSubscription[] = [ { token_id: 1, contract: "0x123", subscribed: true, } satisfies NFTSubscription, // ... ];This provides type safety while still allowing inference.
220-220: Regex may match unintended text.Changing from exact string match to
/Minting Today/increases test flexibility but could match unintended text like "Not Minting Today" or "Minting Today: Postponed".If the exact text is stable, consider a more precise assertion:
expect(screen.getByText("Minting Today")).toBeInTheDocument();Or use a more specific regex if needed:
expect(screen.getByText(/^Minting Today$/)).toBeInTheDocument();
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx(3 hunks)__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.toggle.test.tsx(1 hunks)
🧰 Additional context used
📓 Path-based instructions (11)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx,js,jsx}: Do not include any comments in the code; it should be self-explanatory
Write correct, up-to-date, bug-free, fully componentized, secure, and efficient code
Include all required imports and ensure proper naming of key components
Use NextJS features that match the current version
**/*.{ts,tsx,js,jsx}: Replace<img>elements with<Image />fromnext/imageto satisfy@next/next/no-img-elementESLint rule
Use<Link href="/path">from Next.js for internal navigation instead of plain HTML links to satisfy@next/next/no-html-link-for-pagesESLint rule
Files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.toggle.test.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx
**/*.{tsx,jsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{tsx,jsx}: Use FontAwesome for icons in React components
Use TailwindCSS for styling in React components
Use react-query for data fetching
Always addreadonlybefore props in React components
Files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.toggle.test.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks)
Use framework APIs: internal links should use<Link>, images should usenext/image, and adopt Next's ESLint rules (Core Web Vitals)
**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks rules)
Follow existing code style and naming conventions; maintain clean code standards (measured by SonarQube)
Files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.toggle.test.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Must passtsc --noEmittype checking
Prefer direct named imports for React hooks and types (import { useMemo, useRef, FC, etc. } from "react") overReact.namespace usage (React.useMemo,React.useRef, etc.)
If thereact-hooks/exhaustive-depslint rule is triggered: if the Effect only derives state, remove the Effect and compute during render; if listening to an external system and needing fresh props/state, wrap non-reactive logic inuseEffectEvent
**/*.{ts,tsx}: Must passtsc --noEmitfor TypeScript type checking
Prefer Server Components over Client Components; use Server Functions/Server Actions ('use server') for mutations
Remove unnecessary Effects; if Effect only derives state, compute during render instead
UseuseEffectEventfor non-reactive logic inside Effects to avoid unnecessary re-runs
Use framework APIs:<Link>for internal links,next/imagefor images, adopt Next's ESLint rules
Use'use cache'directive and Cache Components features for explicit opt-in caching in Next.js 16
Use TypeScript and React functional components with hooks
When parsing Seize URLs or similar, fail fast if base origin is unavailable; do not fall back to placeholder origins
Replace<img>elements with<Image />fromnext/image
Use<Link href="/path">for internal navigation instead of plain HTML links
Move data fetches to Server Components; handle mutations through Server Functions/Server Actions with'use server'directive
Files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.toggle.test.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx
**/@(__tests__|*.test).{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Tests should live in
__tests__/orComponentName.test.tsx; mock external dependencies and APIs in tests
Files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.toggle.test.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx
**/__tests__/**/*.{ts,tsx}
📄 CodeRabbit inference engine (GEMINI.md)
Place tests in
__tests__/directory or asComponentName.test.tsxalongside components
Files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.toggle.test.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx
**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (GEMINI.md)
Mock external dependencies and APIs in tests
Files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.toggle.test.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx
__tests__/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (tests/AGENTS.md)
Use Jest +
ts-jestfor TypeScript testing
Files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.toggle.test.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx
__tests__/**/*.{ts,tsx}
📄 CodeRabbit inference engine (tests/AGENTS.md)
Functions must have ≤ 15 cognitive complexity; extract deep ternaries (>3 levels) and break down complex logic
Files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.toggle.test.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx
__tests__/**/*.{ts,tsx,js}
📄 CodeRabbit inference engine (tests/AGENTS.md)
__tests__/**/*.{ts,tsx,js}: Preferfor...ofloops overforEachas it allowsbreak/continueand works with async/await
Usearray.at(-1)andarray.at(-2)instead of index-based array access for negative indexing
UseString.prototype.replaceAll()instead ofreplace()for global string replacements
UseglobalThis.fetch()instead of directfetch()calls
Organize imports with one import per module in order: external → internal → types, with no duplicates
Useelement.remove()instead ofparent.removeChild(element)for DOM manipulation
Catch errors only when meaningful; no empty catch blocks; log errors with context
Avoid double negatives in code; prefer explicit logic and remove redundant annotations; use optional chaining (?.)
Files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.toggle.test.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx
__tests__/**/{components,contexts,hooks}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (tests/AGENTS.md)
Use semantic HTML elements (
<label>,<output>) over ARIA attributes when possible; every form control must have a label
Files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.toggle.test.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx
🧠 Learnings (5)
📚 Learning: 2025-12-05T10:55:30.871Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-05T10:55:30.871Z
Learning: Applies to **/*.test.{ts,tsx} : Mock external dependencies and APIs in tests
Applied to files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.toggle.test.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/__tests__/**/components/**/*.test.{ts,tsx} : Use testing-library/react + user-event for React component tests
Applied to files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.toggle.test.tsx
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: Applies to **/@(__tests__|*.test).{ts,tsx} : Tests should live in `__tests__/` or `ComponentName.test.tsx`; mock external dependencies and APIs in tests
Applied to files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.toggle.test.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx
📚 Learning: 2025-11-25T08:35:58.729Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T08:35:58.729Z
Learning: Applies to **/*.{tsx,jsx} : Use react-query for data fetching
Applied to files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.toggle.test.tsx
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/__tests__/**/components/**/*.test.{ts,tsx} : Test accessibility with keyboard navigation and screen reader compatibility
Applied to files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (javascript-typescript)
Signed-off-by: prxt6529 <prxt@6529.io>
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx (1)
9-16: Consider verifying cache invalidation behavior.The
mockInvalidateQueriesmock is properly set up but never asserted in any test case. IfUserPageSubscriptionsUpcomingis expected to invalidate queries (e.g., after subscription changes), consider adding test coverage to verify this behavior.Example test case to verify invalidation
it("invalidates queries after subscription toggle", async () => { renderComponent(); const toggleButton = screen.getAllByRole("button").find(btn => btn.getAttribute("aria-label")?.includes("Toggle subscription") ); if (toggleButton && !toggleButton.hasAttribute("disabled")) { fireEvent.click(toggleButton); await waitFor(() => { expect(mockInvalidateQueries).toHaveBeenCalled(); }); } });
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
generated/models/ObjectSerializer.tsis excluded by!**/generated/**
📒 Files selected for processing (2)
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx(3 hunks)__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.toggle.test.tsx(3 hunks)
🧰 Additional context used
📓 Path-based instructions (11)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx,js,jsx}: Do not include any comments in the code; it should be self-explanatory
Write correct, up-to-date, bug-free, fully componentized, secure, and efficient code
Include all required imports and ensure proper naming of key components
Use NextJS features that match the current version
**/*.{ts,tsx,js,jsx}: Replace<img>elements with<Image />fromnext/imageto satisfy@next/next/no-img-elementESLint rule
Use<Link href="/path">from Next.js for internal navigation instead of plain HTML links to satisfy@next/next/no-html-link-for-pagesESLint rule
Files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.toggle.test.tsx
**/*.{tsx,jsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{tsx,jsx}: Use FontAwesome for icons in React components
Use TailwindCSS for styling in React components
Use react-query for data fetching
Always addreadonlybefore props in React components
Files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.toggle.test.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks)
Use framework APIs: internal links should use<Link>, images should usenext/image, and adopt Next's ESLint rules (Core Web Vitals)
**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks rules)
Follow existing code style and naming conventions; maintain clean code standards (measured by SonarQube)
Files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.toggle.test.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Must passtsc --noEmittype checking
Prefer direct named imports for React hooks and types (import { useMemo, useRef, FC, etc. } from "react") overReact.namespace usage (React.useMemo,React.useRef, etc.)
If thereact-hooks/exhaustive-depslint rule is triggered: if the Effect only derives state, remove the Effect and compute during render; if listening to an external system and needing fresh props/state, wrap non-reactive logic inuseEffectEvent
**/*.{ts,tsx}: Must passtsc --noEmitfor TypeScript type checking
Prefer Server Components over Client Components; use Server Functions/Server Actions ('use server') for mutations
Remove unnecessary Effects; if Effect only derives state, compute during render instead
UseuseEffectEventfor non-reactive logic inside Effects to avoid unnecessary re-runs
Use framework APIs:<Link>for internal links,next/imagefor images, adopt Next's ESLint rules
Use'use cache'directive and Cache Components features for explicit opt-in caching in Next.js 16
Use TypeScript and React functional components with hooks
When parsing Seize URLs or similar, fail fast if base origin is unavailable; do not fall back to placeholder origins
Replace<img>elements with<Image />fromnext/image
Use<Link href="/path">for internal navigation instead of plain HTML links
Move data fetches to Server Components; handle mutations through Server Functions/Server Actions with'use server'directive
Files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.toggle.test.tsx
**/@(__tests__|*.test).{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Tests should live in
__tests__/orComponentName.test.tsx; mock external dependencies and APIs in tests
Files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.toggle.test.tsx
**/__tests__/**/*.{ts,tsx}
📄 CodeRabbit inference engine (GEMINI.md)
Place tests in
__tests__/directory or asComponentName.test.tsxalongside components
Files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.toggle.test.tsx
**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (GEMINI.md)
Mock external dependencies and APIs in tests
Files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.toggle.test.tsx
__tests__/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (tests/AGENTS.md)
Use Jest +
ts-jestfor TypeScript testing
Files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.toggle.test.tsx
__tests__/**/*.{ts,tsx}
📄 CodeRabbit inference engine (tests/AGENTS.md)
Functions must have ≤ 15 cognitive complexity; extract deep ternaries (>3 levels) and break down complex logic
Files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.toggle.test.tsx
__tests__/**/*.{ts,tsx,js}
📄 CodeRabbit inference engine (tests/AGENTS.md)
__tests__/**/*.{ts,tsx,js}: Preferfor...ofloops overforEachas it allowsbreak/continueand works with async/await
Usearray.at(-1)andarray.at(-2)instead of index-based array access for negative indexing
UseString.prototype.replaceAll()instead ofreplace()for global string replacements
UseglobalThis.fetch()instead of directfetch()calls
Organize imports with one import per module in order: external → internal → types, with no duplicates
Useelement.remove()instead ofparent.removeChild(element)for DOM manipulation
Catch errors only when meaningful; no empty catch blocks; log errors with context
Avoid double negatives in code; prefer explicit logic and remove redundant annotations; use optional chaining (?.)
Files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.toggle.test.tsx
__tests__/**/{components,contexts,hooks}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (tests/AGENTS.md)
Use semantic HTML elements (
<label>,<output>) over ARIA attributes when possible; every form control must have a label
Files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.toggle.test.tsx
🧠 Learnings (9)
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: Applies to **/@(__tests__|*.test).{ts,tsx} : Tests should live in `__tests__/` or `ComponentName.test.tsx`; mock external dependencies and APIs in tests
Applied to files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.toggle.test.tsx
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/__tests__/**/*.test.{ts,tsx,js} : Keep tests independent, deterministic, and fast with production-like data
Applied to files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx
📚 Learning: 2025-12-05T10:55:30.871Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-05T10:55:30.871Z
Learning: Applies to **/*.test.{ts,tsx} : Mock external dependencies and APIs in tests
Applied to files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.toggle.test.tsx
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/*.{ts,tsx,js} : Avoid double negatives in code; prefer explicit logic and remove redundant annotations; use optional chaining (`?.`)
Applied to files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.toggle.test.tsx
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/__tests__/**/components/**/*.test.{ts,tsx} : Test accessibility with keyboard navigation and screen reader compatibility
Applied to files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/__tests__/**/components/**/*.test.{ts,tsx} : Use testing-library/react + user-event for React component tests
Applied to files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.toggle.test.tsx
📚 Learning: 2025-11-25T08:35:58.729Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T08:35:58.729Z
Learning: Applies to **/*.{tsx,jsx} : Use react-query for data fetching
Applied to files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.toggle.test.tsx
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: Fix with modernization (no `// eslint-disable` unless explicitly instructed); prefer refactors aligned with React 19.2, React Compiler, and Next.js 16 conventions
Applied to files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.toggle.test.tsx
📚 Learning: 2025-11-25T08:37:21.058Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __mocks__/AGENTS.md:0-0
Timestamp: 2025-11-25T08:37:21.058Z
Learning: Applies to __mocks__/**/__mocks__/** : Document expected behaviour in the mock file when it is not obvious
Applied to files:
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.toggle.test.tsx
🧬 Code graph analysis (1)
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx (1)
generated/models/SubscriptionDetails.ts (1)
SubscriptionDetails(15-71)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (7)
__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.test.tsx (3)
4-5: LGTM! Migration to generated models completed.The imports now use generated model types, aligning with the PR's objective to replace legacy subscription types with generated models.
88-97: Excellent fix for test determinism!The introduction of
FIXED_TIMESTAMPresolves the previous review concern about non-deterministic test data fromDate.now(). The mock data now includes all required fields from the generatedSubscriptionDetailsmodel with stable, predictable values.
222-222: Good use of regex for flexible text matching.Using
/Minting Today/instead of an exact string match makes the assertion more resilient to minor text variations (e.g., punctuation changes or surrounding text).__tests__/components/user/subscriptions/UserPageSubscriptionsUpcoming.toggle.test.tsx (4)
12-17: Previous review feedback addressed successfully.The mock is now properly captured as
mockInvalidateQueries, enabling assertions on cache invalidation behavior. This resolves the inconsistency noted in the previous review.
48-50: Good addition for test isolation.Adding
beforeEachto clear mocks ensures each test runs with a clean slate and prevents false positives from accumulated mock call history.
77-77: Clean refactor removing unused return value.Since the test uses
screenqueries and doesn't referencecontainer, removing the capture is appropriate.
80-84: Assertion correctly verifies cache invalidation.The assertion structure properly validates that subscription-related queries are invalidated after toggling. The flexible matching strategy using
stringContainingis appropriate and doesn't over-couple the test to implementation details.
|



Summary by CodeRabbit
New Features
Style
Bug Fixes
Tests
✏️ Tip: You can customize this high-level summary in your review settings.