Skip to content

Distribution photos - Direct S3 upload#1697

Merged
prxt6529 merged 8 commits intomainfrom
distribution-photos-direct-s3-upload
Jan 5, 2026
Merged

Distribution photos - Direct S3 upload#1697
prxt6529 merged 8 commits intomainfrom
distribution-photos-direct-s3-upload

Conversation

@prxt6529
Copy link
Copy Markdown
Collaborator

@prxt6529 prxt6529 commented Jan 5, 2026

Summary by CodeRabbit

  • New Features

    • Multipart photo uploads for large files (up to 500MB) with overall progress and completion handling.
    • New API endpoints to support photo upload multipart and completion flows; distribution overview endpoint added.
  • Improvements

    • Subscription downloads simplified to a single-click flow with loading feedback and clearer success/error toasts.
    • Reset and finalize actions now call APIs directly and show immediate toast results.
    • CSV parsing now detects and skips headers for automatic airdrops.
    • Page title updates when viewing a distribution.
  • Tests

    • Tests updated for token-confirmation gating and new direct download/upload flows.

✏️ Tip: You can customize this high-level summary in your review settings.

Signed-off-by: prxt6529 <prxt@6529.io>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 5, 2026

📝 Walkthrough

Walkthrough

Replaces modal-confirm flows with direct action handlers; requires confirmed token IDs for subscription actions; centralizes multipart upload core with retries/concurrency and exports uploadDistributionPhotos; adds OpenAPI photo endpoints; raises upload limit to 500MB; adds CSV header-aware parsing; updates tests and title-setting effect.

Changes

Cohort / File(s) Summary
Tests — subscription & token flows
__tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscription.test.tsx, __tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscriptionFooter.test.tsx, __tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscriptionFooterConfirmTokenId.test.tsx
Remove SubscriptionConfirm mocks/usage; adapt tests for confirmedTokenId vs no-token contexts; expect onConfirm(tokenId) signature; assert direct download/upload flows, loading states, and updated toast outcomes.
Component — subscription download
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscription.tsx
Remove modal-driven SubscriptionConfirm; require confirmedTokenId to render; add handleDownload invoking download(...); show CircleLoader while downloading; centralized toast/error handling.
Component — footer actions & uploads
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx, components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsx
Inline reset/finalize API calls; add getErrorMessage helper; replace FormData POST with uploadDistributionPhotos; increase MAX_FILE_SIZE to 500MB and update error message.
Component — automatic airdrops CSV parsing
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterAutomaticAirdrops.tsx
Add isHeaderRow; update parseCsv to return { rows, hadHeader }; detect/skip CSV header, compute lineOffset for error lines, lowercase addresses, and update UI hint to address,value.
Service — distribution photo uploads
services/distribution/distributionPhotoUpload.ts
New uploadDistributionPhotos({ contract, tokenId, files, onProgress }): small-file presigned PUT path; multipart path for large files using 5MB parts, concurrency, per-part retries (p-retry), aggregated progress, and final complete call returning media URLs.
Core — multipart upload utility
services/uploads/multipartUploadCore.ts
New multipartUploadCore({ file, endpoints, onProgress }) and getContentType(file): centralized start/part/complete flow with chunking, concurrency, retries, ETag collection, and final media_url return.
Integration — wave multipart refactor
components/waves/create-wave/services/multiPartUpload.ts
Replace inlined multipart implementation with multipartUploadCore call; preserve multiPartUpload public signature and progress semantics.
API Spec — photo upload endpoints
openapi.yaml
Add /distribution_photos/{contract}/{nft_id} endpoints (/prep, /multipart-upload, /multipart-upload/part, /multipart-upload/completion, /complete) and new schemas: DistributionPhotoCompleteRequestPhoto, DistributionPhotoCompleteRequest, DistributionPhotoCompleteResponse, DistributionOverview.
UI — title side effect
components/distribution/Distribution.tsx
Add useTitle and effect to set page title to "{header} #{nftId}
Misc — imports & call sites
various (components/..., __tests__/...)
Remove unused imports, update call sites for new parseCsv return shape and uploadDistributionPhotos, align tests/mocks and button/modal UX with new signatures and flows.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Browser
    participant Frontend
    participant Backend
    participant S3

    Note over User,Browser: User selects files and clicks Upload
    User->>Browser: choose files & click Upload
    Browser->>Frontend: uploadDistributionPhotos({contract, tokenId, files, onProgress})

    alt All files small (presigned PUT)
        Frontend->>Backend: POST /distribution_photos/{contract}/{tokenId}/prep
        Backend-->>Frontend: { upload_url }
        Frontend->>S3: PUT file to presigned URL (progress)
        S3-->>Frontend: 200 OK (returns media_url)
    else Any large files (multipart)
        Frontend->>Backend: POST /distribution_photos/{contract}/{tokenId}/multipart-upload (start)
        Backend-->>Frontend: { upload_id, key }
        par Upload parts concurrently
            loop per part
                Frontend->>Backend: POST /multipart-upload/part (get part URL)
                Backend-->>Frontend: { part_presigned_url }
                Frontend->>S3: PUT part to presigned URL (part progress, returns ETag)
                S3-->>Frontend: 200 + ETag
            end
        end
        Frontend->>Backend: POST /multipart-upload/completion (submit parts ETags)
        Backend-->>Frontend: { media_url }
    end

    Frontend->>Backend: POST /distribution_photos/{contract}/{tokenId}/complete (save media URLs)
    Backend-->>Frontend: 200 OK
    Frontend-->>Browser: resolve with media URLs, emit final onProgress
    Browser->>User: show success toast & refresh overview
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • ragnep
  • simo6529
  • kmk142789

Poem

🐰 I hopped through code with nimble paws,

Sliced the files and dodged the straws.
Headers skipped and parts aligned,
To S3’s shores the photos twined.
Hooray — the uploads danced just fine!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main objective of the pull request: implementing direct S3 upload functionality for distribution photos, which is evident from the new multipart upload service, updated components, and OpenAPI schema additions.
✨ Finishing touches
  • 📝 Generate docstrings

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

❤️ Share

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Fix all issues with AI Agents 🤖
In @services/distribution/distributionPhotoUpload.ts:
- Around line 192-201: Guard against division by zero in the progress
calculation by handling the case where totalSize === 0 before computing percent:
compute totalSize from files as now, but in handleProgress (or when calling
onProgress) check if totalSize is 0 and if so call onProgress(100) (or 0
depending on desired semantics) instead of performing (overallUploaded /
totalSize); update the handleProgress function that references totalSize,
overallUploaded, files, and onProgress to short-circuit when totalSize is zero
to avoid division by zero.
- Around line 109-149: The progress delta bug is caused by lastChunkLoaded
persisting across retries; inside uploadChunk (or immediately before the
axios.put call) reset lastChunkLoaded to 0 or use a local per-attempt variable
(e.g., lastChunkLoadedLocal) so the onUploadProgress callback computes deltas
from the start of the current attempt; ensure this change is applied for each
part upload (references: lastChunkLoaded, uploadChunk, onUploadProgress,
axios.put, partNumber, blobPart) so retries don't inherit prior loaded values or
pollute other part uploads.
🧹 Nitpick comments (7)
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscription.tsx (1)

77-91: Minor: Mixed utility class conventions.

The button mixes Bootstrap classes (d-flex, gap-2, align-items-center) with Tailwind classes (tw-*). Consider using consistent class conventions.

🔎 Suggested refactor for consistency
       {downloading ? (
-        <span className="d-flex gap-2 align-items-center">
+        <span className="tw-flex tw-gap-2 tw-items-center">
           <CircleLoader />
           <span>Downloading</span>
         </span>
services/distribution/distributionPhotoUpload.ts (1)

205-217: Consider parallel file uploads for better performance.

Files are uploaded sequentially. For multiple large files, parallel uploads (with bounded concurrency) could significantly improve overall upload time.

components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterAutomaticAirdrops.tsx (1)

234-234: Consider adding a header row to the example for clarity.

The format hint shows address,value but the example doesn't include a header row. Adding a header to the example would demonstrate that headers are supported.

🔎 Suggested example update
                   <code>
-                    {`0x33FD426905F149f8376e227d0C9D3340AaD17aF1,5
+                    {`address,value
+0x33FD426905F149f8376e227d0C9D3340AaD17aF1,5
 0x9f6ae0370d74f0e591c64cec4a8ae0d627817014,10`}
                   </code>
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx (1)

379-383: Inconsistent error typing: use unknown instead of any.

Other error handlers in this file use error: unknown (lines 98, 141, 324), but this one uses error: any. For consistency and type safety, prefer unknown.

🔎 Proposed fix
-              } catch (error: any) {
+              } catch (error: unknown) {
                 const errorMessage =
                   typeof error === "string"
                     ? error
-                    : error?.message || "Something went wrong.";
+                    : error instanceof Error
+                    ? error.message
+                    : "Something went wrong.";
openapi.yaml (2)

329-518: Distribution-photo upload endpoints are consistent with existing media APIs

The new /distribution_photos/{contract}/{nft_id} upload and multipart endpoints correctly mirror the existing /drop-media and /wave-media patterns (same request/response schemas, 201 status, and auth description). Consider optionally adding explicit 404 responses for non‑existent contract/nft_id to match other resource-specific paths, but this is not required for correctness.


9131-9161: Completion request/response schemas for distribution photos look sound

DistributionPhotoCompleteRequestPhoto and DistributionPhotoCompleteRequest enforce at least one media_url, and DistributionPhotoCompleteResponse returns a simple { success, photos: string[] } payload, which is easy to consume and aligns with the /complete endpoint semantics. The CloudFront URL descriptions are clear; just ensure backend responses actually use that host.

__tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscriptionFooter.test.tsx (1)

45-55: Footer tests comprehensively cover the new direct admin flows

  • The ConfirmTokenIdModal mock now calling props.onConfirm("123") aligns with the simplified onConfirm(tokenId: string) contract and drives the admin state in TestWrapper without coupling to the real modal.
  • shows upload modals when upload buttons clicked correctly verifies that both the Distribution Photos and Automatic Airdrops modals are wired and toggle from the new buttons.
  • The resetSubscriptions and finalizeDistribution tests properly mock commonApiPost/commonApiFetch, assert that the right endpoints (containing /reset and /normalize) are hit, and confirm the success toasts, so the new non‑modal flows are well covered and external APIs are mocked as per testing guidelines. Based on learnings, this matches the “mock external dependencies and APIs in tests” requirement.

If you want slightly stricter isolation, you could clear authCtx.setToast in a beforeEach for this file so toast expectations never depend on calls from earlier tests, but it’s not strictly necessary.

Also applies to: 134-156, 176-246

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 839f17b and 96d0896.

⛔ Files ignored due to path filters (4)
  • generated/models/DistributionPhotoCompleteRequest.ts is excluded by !**/generated/**
  • generated/models/DistributionPhotoCompleteRequestPhoto.ts is excluded by !**/generated/**
  • generated/models/DistributionPhotoCompleteResponse.ts is excluded by !**/generated/**
  • generated/models/ObjectSerializer.ts is excluded by !**/generated/**
📒 Files selected for processing (9)
  • __tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscription.test.tsx
  • __tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscriptionFooter.test.tsx
  • __tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscriptionFooterConfirmTokenId.test.tsx
  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscription.tsx
  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx
  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterAutomaticAirdrops.tsx
  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsx
  • openapi.yaml
  • services/distribution/distributionPhotoUpload.ts
🧰 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}: Remove unnecessary Effects. If the Effect's only job is to derive or sync internal state, calculate during render or use useMemo instead.
Use useEffectEvent for non-reactive logic inside Effects to read the latest props/state without turning them into dependencies or causing unnecessary re-runs.
Use explicit caching with "use cache" directive at the top of Server Components, routes, or functions. Configure cacheComponents: true in next.config.ts as needed.

**/*.{ts,tsx,js,jsx}: Remove unnecessary Effects; if the Effect only derives state, compute during render instead
Use useEffectEvent when listening to external events but needing the latest props/state without re-running the Effect
Move data fetching from client Effects to Server Components; mutations go through Server Actions ('use server')

Files:

  • __tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscriptionFooterConfirmTokenId.test.tsx
  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsx
  • services/distribution/distributionPhotoUpload.ts
  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx
  • __tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscriptionFooter.test.tsx
  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterAutomaticAirdrops.tsx
  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscription.tsx
  • __tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscription.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 add readonly before props in React components

**/*.{tsx,jsx}: Use internal links via <Link> component from Next.js instead of <a> tags
Replace <img> elements with <Image /> from next/image

Files:

  • __tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscriptionFooterConfirmTokenId.test.tsx
  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsx
  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx
  • __tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscriptionFooter.test.tsx
  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterAutomaticAirdrops.tsx
  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscription.tsx
  • __tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscription.test.tsx
**/*.{test,spec}.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

Run npm run test to execute all Jest tests and enforce ≥80% line coverage for files changed since main. Tests must pass and coverage threshold must be met before completing any task.

Enforce ≥ 80% line coverage for files changed since main by running npm run test

Files:

  • __tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscriptionFooterConfirmTokenId.test.tsx
  • __tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscriptionFooter.test.tsx
  • __tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscription.test.tsx
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

Run npm run lint to ensure code satisfies ESLint (Next's Core Web Vitals + React Hooks). Code must pass linting before completing any task.

**/*.{js,ts,jsx,tsx}: Code must satisfy ESLint with Next's Core Web Vitals and React Hooks rules by running npm run lint
Do not add eslint-disable comments unless explicitly instructed; prefer refactors aligned with React 19.2, React Compiler, and Next.js 16 conventions

Files:

  • __tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscriptionFooterConfirmTokenId.test.tsx
  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsx
  • services/distribution/distributionPhotoUpload.ts
  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx
  • __tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscriptionFooter.test.tsx
  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterAutomaticAirdrops.tsx
  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscription.tsx
  • __tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscription.test.tsx
**/*.{jsx,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

**/*.{jsx,tsx}: Replace <img> elements with <Image /> from next/image to comply with Next.js ESLint rule @next/next/no-img-element.
Use <Link href="/path"> from Next.js for internal navigation instead of raw <a> tags to comply with @next/next/no-html-link-for-pages.

Files:

  • __tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscriptionFooterConfirmTokenId.test.tsx
  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsx
  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx
  • __tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscriptionFooter.test.tsx
  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterAutomaticAirdrops.tsx
  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscription.tsx
  • __tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscription.test.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

Use TypeScript with React functional components and hooks. Follow existing code style and naming conventions.

Files:

  • __tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscriptionFooterConfirmTokenId.test.tsx
  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsx
  • services/distribution/distributionPhotoUpload.ts
  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx
  • __tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscriptionFooter.test.tsx
  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterAutomaticAirdrops.tsx
  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscription.tsx
  • __tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscription.test.tsx
**/*.{tsx,ts}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript with React functional components and hooks

Files:

  • __tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscriptionFooterConfirmTokenId.test.tsx
  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsx
  • services/distribution/distributionPhotoUpload.ts
  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx
  • __tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscriptionFooter.test.tsx
  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterAutomaticAirdrops.tsx
  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscription.tsx
  • __tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscription.test.tsx
**/{__tests__,*.test.{tsx,ts}}

📄 CodeRabbit inference engine (AGENTS.md)

Place tests in __tests__/ directory or as ComponentName.test.tsx alongside the component

Files:

  • __tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscriptionFooterConfirmTokenId.test.tsx
  • __tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscriptionFooter.test.tsx
  • __tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscription.test.tsx
**/*.{test,spec}.{tsx,ts}

📄 CodeRabbit inference engine (AGENTS.md)

Mock external dependencies and APIs in tests

Files:

  • __tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscriptionFooterConfirmTokenId.test.tsx
  • __tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscriptionFooter.test.tsx
  • __tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscription.test.tsx
**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (AGENTS.md)

Prefer direct named imports from React (useMemo, useRef, FC) over React. namespace usage

Files:

  • __tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscriptionFooterConfirmTokenId.test.tsx
  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsx
  • services/distribution/distributionPhotoUpload.ts
  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx
  • __tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscriptionFooter.test.tsx
  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterAutomaticAirdrops.tsx
  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscription.tsx
  • __tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscription.test.tsx
**/*.{ts,js}

📄 CodeRabbit inference engine (AGENTS.md)

When parsing Seize URLs or similar, fail fast if base origin is unavailable instead of falling back to placeholder origins

Files:

  • services/distribution/distributionPhotoUpload.ts
🧠 Learnings (3)
📚 Learning: 2025-12-30T14:31:52.993Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-30T14:31:52.993Z
Learning: Applies to **/__tests__/**/*.{test,spec}.{ts,tsx,js,jsx}|**/*.{test,spec}.{ts,tsx,js,jsx} : Place tests in `__tests__/` directory or as `ComponentName.test.tsx` files. Mock external dependencies and APIs in tests.

Applied to files:

  • __tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscriptionFooter.test.tsx
  • __tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscription.test.tsx
📚 Learning: 2025-12-30T14:32:37.293Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-30T14:32:37.293Z
Learning: Applies to __tests__/**/*.{ts,tsx,js},!**/__tests__/**,!**/__mocks__/**,!**/*.d.ts : Avoid double negatives in code; prefer explicit statements and use optional chaining (`?.`)

Applied to files:

  • __tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscriptionFooter.test.tsx
  • __tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscription.test.tsx
📚 Learning: 2025-12-30T14:32:19.339Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-30T14:32:19.339Z
Learning: Applies to **/*.{test,spec}.{tsx,ts} : Mock external dependencies and APIs in tests

Applied to files:

  • __tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscription.test.tsx
🧬 Code graph analysis (4)
services/distribution/distributionPhotoUpload.ts (7)
generated/models/ApiCreateMediaUploadUrlRequest.ts (1)
  • ApiCreateMediaUploadUrlRequest (16-44)
generated/models/ApiCreateMediaUrlResponse.ts (1)
  • ApiCreateMediaUrlResponse (16-51)
generated/models/ApiStartMultipartMediaUploadResponse.ts (1)
  • ApiStartMultipartMediaUploadResponse (16-44)
generated/models/ApiUploadPartOfMultipartUploadRequest.ts (1)
  • ApiUploadPartOfMultipartUploadRequest (16-51)
generated/models/ApiCompleteMultipartUploadRequest.ts (1)
  • ApiCompleteMultipartUploadRequest (17-52)
generated/models/ApiCompleteMultipartUploadResponse.ts (1)
  • ApiCompleteMultipartUploadResponse (16-37)
generated/models/DistributionPhotoCompleteRequest.ts (1)
  • DistributionPhotoCompleteRequest (17-38)
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx (1)
services/distribution/distributionPhotoUpload.ts (1)
  • uploadDistributionPhotos (186-230)
__tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscriptionFooter.test.tsx (2)
tests/testHelpers.ts (1)
  • test (4-11)
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscription.tsx (1)
  • isSubscriptionsAdmin (94-100)
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscription.tsx (3)
components/distribution-plan-tool/review-distribution-plan/table/constants.ts (1)
  • PUBLIC_SUBSCRIPTIONS_PHASE_ID (1-1)
constants.ts (1)
  • MEMES_CONTRACT (5-5)
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 (16)
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterUploadPhotos.tsx (1)

38-38: LGTM! File size limit increase aligns with new multipart upload support.

The 500MB limit is appropriate given the new distributionPhotoUpload.ts service that handles multipart uploads for files ≥ 5MB, enabling efficient upload of large distribution photos.

Also applies to: 61-63

__tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscription.test.tsx (2)

66-72: LGTM! Test contexts properly reflect the new token-confirmation gating.

The mock contexts (mockDistCtxWithTokenId and mockDistCtxNoTokenId) correctly test both states for the new gating condition requiring confirmedTokenId.


197-244: LGTM! Loading state test correctly uses deferred promise pattern.

The test properly verifies the loading state by controlling promise resolution timing. The pattern ensures the "Downloading" text appears during the async operation.

components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscription.tsx (3)

40-46: LGTM! Proper gating with confirmedTokenId requirement.

The component correctly returns early when the token ID is not confirmed, preventing download attempts without a valid token ID.


51-74: LGTM! Clean direct download handler with proper error handling.

The handleDownload function correctly manages loading state, invokes the download, and provides toast feedback for both success and error cases.


102-125: LGTM! Download function with comprehensive error handling.

The function properly returns a result object with success/failure status and handles both successful responses and exceptions gracefully.

services/distribution/distributionPhotoUpload.ts (3)

1-19: LGTM! Well-structured constants and imports.

The constants are appropriately defined:

  • 5MB threshold aligns with S3 multipart minimum
  • 5 concurrent uploads balances throughput and resource usage
  • 3 retries with exponential backoff is a sensible default

35-74: LGTM! Simple upload path is well-implemented.

The function correctly:

  • Requests a presigned URL from the API
  • Uploads with proper Content-Type header
  • Tracks upload progress via delta calculation
  • Validates required response fields

142-146: Verify S3 bucket CORS configuration exposes the ETag header.

The ETag header must be accessible from the S3 presigned URL response. S3 presigned URLs do not bypass CORS enforcement, so the bucket's CORS configuration must include ETag in the ExposeHeaders list. The code correctly fails fast if the ETag is missing, but this depends entirely on proper backend S3 configuration.

components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterAutomaticAirdrops.tsx (3)

49-55: LGTM! Header detection is user-friendly.

The isHeaderRow function correctly identifies common header patterns (address/wallet, count/value), allowing users to upload CSVs with or without headers.


57-109: LGTM! Parser correctly handles headers and reports accurate line numbers.

The lineOffset calculation ensures error messages reference the correct line number in the original file, accounting for whether a header was present.


169-174: LGTM! CSV reconstruction ensures consistent format.

Rebuilding the CSV from parsed rows guarantees the backend receives a clean, normalized format without headers, regardless of input variations.

components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx (3)

114-155: LGTM! Clean finalize distribution implementation.

The function properly:

  • Manages loading state
  • Posts to the normalize endpoint
  • Handles both success/failure response fields
  • Provides appropriate toast feedback
  • Refreshes overview on success

193-196: LGTM! Direct action is cleaner than modal flow.

The inline onClick handler that directly calls resetSubscriptions simplifies the UX while maintaining the null-check for distributionPlan.


313-323: LGTM! Properly integrated new upload service.

The uploadDistributionPhotos service is correctly used, and the success message appropriately reports the count of uploaded photos.

__tests__/components/distribution-plan-tool/ReviewDistributionPlanTableSubscriptionFooterConfirmTokenId.test.tsx (1)

53-69: Updated onConfirm expectations correctly reflect new callback signature

Asserting mockOnConfirm is called with a single "123" argument for both the Confirm button and Enter key paths matches the simplified onConfirm(tokenId) contract and keeps coverage for both interaction modes.

Comment thread services/distribution/distributionPhotoUpload.ts Outdated
Comment thread services/distribution/distributionPhotoUpload.ts
Signed-off-by: prxt6529 <prxt@6529.io>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
components/waves/create-wave/services/multiPartUpload.ts (1)

28-34: Consider: Division by zero for 0-byte files.

If file.size is 0, the calculation at line 31 would result in division by zero. While 0-byte files are an edge case, the check at line 20 only validates the maximum size, not the minimum.

🔎 Suggested fix
 const handleProgress = (bytesUploaded: number) => {
   overallUploaded += bytesUploaded;
   if (onProgress) {
+    if (file.size === 0) {
+      onProgress(100);
+      return;
+    }
     const percent = Math.floor((overallUploaded / file.size) * 100);
     onProgress(percent);
   }
 };
services/distribution/distributionPhotoUpload.ts (1)

108-120: Sequential upload is intentional but may be slower for multiple files.

Files are uploaded one at a time. If faster multi-file uploads are desired, consider parallel processing with concurrency control (similar to the part uploads in multipartUploadCore). However, sequential uploads provide simpler progress tracking and lower resource usage, so this may be intentional.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 96d0896 and 598d13a.

📒 Files selected for processing (4)
  • components/distribution/Distribution.tsx
  • components/waves/create-wave/services/multiPartUpload.ts
  • services/distribution/distributionPhotoUpload.ts
  • services/uploads/multipartUploadCore.ts
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{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}: Remove unnecessary Effects. If the Effect's only job is to derive or sync internal state, calculate during render or use useMemo instead.
Use useEffectEvent for non-reactive logic inside Effects to read the latest props/state without turning them into dependencies or causing unnecessary re-runs.
Use explicit caching with "use cache" directive at the top of Server Components, routes, or functions. Configure cacheComponents: true in next.config.ts as needed.

**/*.{ts,tsx,js,jsx}: Remove unnecessary Effects; if the Effect only derives state, compute during render instead
Use useEffectEvent when listening to external events but needing the latest props/state without re-running the Effect
Move data fetching from client Effects to Server Components; mutations go through Server Actions ('use server')

Files:

  • components/distribution/Distribution.tsx
  • services/distribution/distributionPhotoUpload.ts
  • services/uploads/multipartUploadCore.ts
  • components/waves/create-wave/services/multiPartUpload.ts
**/*.{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 add readonly before props in React components

**/*.{tsx,jsx}: Use internal links via <Link> component from Next.js instead of <a> tags
Replace <img> elements with <Image /> from next/image

Files:

  • components/distribution/Distribution.tsx
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

Run npm run lint to ensure code satisfies ESLint (Next's Core Web Vitals + React Hooks). Code must pass linting before completing any task.

**/*.{js,ts,jsx,tsx}: Code must satisfy ESLint with Next's Core Web Vitals and React Hooks rules by running npm run lint
Do not add eslint-disable comments unless explicitly instructed; prefer refactors aligned with React 19.2, React Compiler, and Next.js 16 conventions

Files:

  • components/distribution/Distribution.tsx
  • services/distribution/distributionPhotoUpload.ts
  • services/uploads/multipartUploadCore.ts
  • components/waves/create-wave/services/multiPartUpload.ts
**/*.{jsx,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

**/*.{jsx,tsx}: Replace <img> elements with <Image /> from next/image to comply with Next.js ESLint rule @next/next/no-img-element.
Use <Link href="/path"> from Next.js for internal navigation instead of raw <a> tags to comply with @next/next/no-html-link-for-pages.

Files:

  • components/distribution/Distribution.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

Use TypeScript with React functional components and hooks. Follow existing code style and naming conventions.

Files:

  • components/distribution/Distribution.tsx
  • services/distribution/distributionPhotoUpload.ts
  • services/uploads/multipartUploadCore.ts
  • components/waves/create-wave/services/multiPartUpload.ts
**/*.{tsx,ts}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript with React functional components and hooks

Files:

  • components/distribution/Distribution.tsx
  • services/distribution/distributionPhotoUpload.ts
  • services/uploads/multipartUploadCore.ts
  • components/waves/create-wave/services/multiPartUpload.ts
**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (AGENTS.md)

Prefer direct named imports from React (useMemo, useRef, FC) over React. namespace usage

Files:

  • components/distribution/Distribution.tsx
  • services/distribution/distributionPhotoUpload.ts
  • services/uploads/multipartUploadCore.ts
  • components/waves/create-wave/services/multiPartUpload.ts
**/*.{ts,js}

📄 CodeRabbit inference engine (AGENTS.md)

When parsing Seize URLs or similar, fail fast if base origin is unavailable instead of falling back to placeholder origins

Files:

  • services/distribution/distributionPhotoUpload.ts
  • services/uploads/multipartUploadCore.ts
  • components/waves/create-wave/services/multiPartUpload.ts
🧬 Code graph analysis (3)
components/distribution/Distribution.tsx (1)
contexts/TitleContext.tsx (1)
  • useTitle (201-207)
services/distribution/distributionPhotoUpload.ts (5)
generated/models/ApiCreateMediaUploadUrlRequest.ts (1)
  • ApiCreateMediaUploadUrlRequest (16-44)
generated/models/ApiCreateMediaUrlResponse.ts (1)
  • ApiCreateMediaUrlResponse (16-51)
services/uploads/multipartUploadCore.ts (1)
  • multipartUploadCore (63-171)
generated/models/DistributionPhotoCompleteRequest.ts (1)
  • DistributionPhotoCompleteRequest (17-38)
generated/models/DistributionPhotoCompleteResponse.ts (1)
  • DistributionPhotoCompleteResponse (16-44)
components/waves/create-wave/services/multiPartUpload.ts (1)
services/uploads/multipartUploadCore.ts (1)
  • multipartUploadCore (63-171)
⏰ 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/Distribution.tsx (1)

105-109: LGTM!

The useEffect correctly sets the document title when isValidNftId and nftId are truthy, and includes all necessary dependencies. The title format matches the component's display pattern.

services/uploads/multipartUploadCore.ts (4)

1-15: LGTM - Well-structured constants.

The constants are appropriate for S3 multipart uploads (5MB minimum part size, reasonable concurrency and retry settings).


29-61: LGTM - Good fallback handling for content types.

The function properly prioritizes the browser-provided MIME type and falls back to extension-based detection for common media types used in distribution contexts.


96-136: Consider: Progress may over-report on retry.

If a part upload fails mid-way and retries, the onUploadProgress callback will report bytes that were already counted in the failed attempt. This could cause progress to temporarily exceed 100% or display jumpy behavior in the UI.

Since lastChunkLoaded is correctly scoped inside uploadChunk, each retry starts fresh at 0, but cumulative progress across all parts (tracked by callers) doesn't account for the failed attempt's partial bytes.

This is acceptable for most use cases but worth noting if precise progress tracking is required.


148-171: LGTM - Proper completion handling.

The completion request correctly includes all part numbers and ETags, with appropriate validation of the returned media URL.

components/waves/create-wave/services/multiPartUpload.ts (2)

2-5: LGTM - Good refactor to use centralized upload core.

Clean extraction of multipart upload logic to a reusable module, improving maintainability across the codebase.


36-49: LGTM - Clean delegation to core with proper endpoint configuration.

The endpoint paths are correctly constructed and the return type properly maps the media URL and content type.

services/distribution/distributionPhotoUpload.ts (5)

1-9: LGTM - Clean imports and threshold configuration.

The threshold aligns with S3's minimum part size for multipart uploads.


25-64: LGTM - Simple upload path implementation.

Correctly handles presigned URL flow with proper progress delta reporting and validation of server responses.


66-83: LGTM - Clean delegation to multipartUploadCore.

The spread syntax for optional onProgress is a nice touch for conditional inclusion.


94-104: LGTM - Division by zero guard properly implemented.

The totalSize === 0 check at line 97 correctly addresses the edge case where files array is empty or all files have zero size, as noted in previous review.


122-133: No changes needed—the property mapping is correct.

The code correctly maps each URL to { media_url: url }, which matches the DistributionPhotoCompleteRequestPhoto type definition. The type expects media_url as a required string property, as confirmed in both the OpenAPI schema and the generated TypeScript class.

Signed-off-by: prxt6529 <prxt@6529.io>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx (2)

22-30: LGTM - Consider extracting to shared utilities.

The error normalization logic is solid and handles common error types appropriately. This pattern is useful across the codebase and could be extracted to a shared utility file (e.g., helpers/errorUtils.ts) for reuse in other components.


303-330: Consider adding upload progress feedback.

The uploadDistributionPhotos service supports an onProgress callback (per distributionPhotoUpload.ts), but it's not utilized here. Given the PR increases the upload limit to 500MB, users uploading large files won't receive progress feedback during potentially lengthy uploads.

🔎 Proposed enhancement to add progress tracking
+            const [uploadProgress, setUploadProgress] = useState(0);
             ...
             onUpload={async (
               contract: string,
               tokenId: string,
               files: File[]
             ) => {
               setShowUploadPhotos(false);
               setIsUploading(true);
+              setUploadProgress(0);
               try {
                 const uploadedUrls = await uploadDistributionPhotos({
                   contract,
                   tokenId,
                   files,
+                  onProgress: (percent) => setUploadProgress(percent),
                 });

Then display uploadProgress in the button's loading state.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 598d13a and 5beee36.

📒 Files selected for processing (1)
  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{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}: Remove unnecessary Effects. If the Effect's only job is to derive or sync internal state, calculate during render or use useMemo instead.
Use useEffectEvent for non-reactive logic inside Effects to read the latest props/state without turning them into dependencies or causing unnecessary re-runs.
Use explicit caching with "use cache" directive at the top of Server Components, routes, or functions. Configure cacheComponents: true in next.config.ts as needed.

**/*.{ts,tsx,js,jsx}: Remove unnecessary Effects; if the Effect only derives state, compute during render instead
Use useEffectEvent when listening to external events but needing the latest props/state without re-running the Effect
Move data fetching from client Effects to Server Components; mutations go through Server Actions ('use server')

Files:

  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.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 add readonly before props in React components

**/*.{tsx,jsx}: Use internal links via <Link> component from Next.js instead of <a> tags
Replace <img> elements with <Image /> from next/image

Files:

  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

Run npm run lint to ensure code satisfies ESLint (Next's Core Web Vitals + React Hooks). Code must pass linting before completing any task.

**/*.{js,ts,jsx,tsx}: Code must satisfy ESLint with Next's Core Web Vitals and React Hooks rules by running npm run lint
Do not add eslint-disable comments unless explicitly instructed; prefer refactors aligned with React 19.2, React Compiler, and Next.js 16 conventions

Files:

  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx
**/*.{jsx,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

**/*.{jsx,tsx}: Replace <img> elements with <Image /> from next/image to comply with Next.js ESLint rule @next/next/no-img-element.
Use <Link href="/path"> from Next.js for internal navigation instead of raw <a> tags to comply with @next/next/no-html-link-for-pages.

Files:

  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

Use TypeScript with React functional components and hooks. Follow existing code style and naming conventions.

Files:

  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx
**/*.{tsx,ts}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript with React functional components and hooks

Files:

  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx
**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (AGENTS.md)

Prefer direct named imports from React (useMemo, useRef, FC) over React. namespace usage

Files:

  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx
🧬 Code graph analysis (1)
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx (1)
services/distribution/distributionPhotoUpload.ts (1)
  • uploadDistributionPhotos (85-133)
⏰ 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/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx (4)

118-153: LGTM!

The finalizeDistribution function is well-structured with proper loading state management, dual-layer error handling (API response errors and network exceptions), and correct state refresh on success.


191-194: Verify: Intentional removal of confirmation for destructive action?

The reset button now directly invokes resetSubscriptions without a confirmation dialog. Since this is a destructive operation that resets subscription data, accidental clicks could cause data loss.

Please confirm this is intentional. If not, consider adding a confirmation step (e.g., window.confirm() or a modal) before proceeding with the reset.


263-264: LGTM!

Direct invocation for finalize is appropriate as it's an idempotent operation. The button is properly disabled during the async operation.


371-375: LGTM!

Consistent error handling using the getErrorMessage helper.

Signed-off-by: prxt6529 <prxt@6529.io>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterAutomaticAirdrops.tsx (1)

57-110: Clarify terminology in error messages to match header detection flexibility.

The code's isHeaderRow function (line 53) accepts both "count" and "value" as valid second column names, but error message at line 81 hardcodes "address,count". When the UI hint at line 235 shows "address,value", users may submit a valid CSV with that header, only to see an error message referencing "count" instead, creating confusion.

Update the error message to reflect both accepted formats (e.g., "Expected format "address,count" or "address,value"") or standardize terminology across all user-facing text and error messages.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5beee36 and 7bf8803.

📒 Files selected for processing (1)
  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterAutomaticAirdrops.tsx
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{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}: Remove unnecessary Effects. If the Effect's only job is to derive or sync internal state, calculate during render or use useMemo instead.
Use useEffectEvent for non-reactive logic inside Effects to read the latest props/state without turning them into dependencies or causing unnecessary re-runs.
Use explicit caching with "use cache" directive at the top of Server Components, routes, or functions. Configure cacheComponents: true in next.config.ts as needed.

**/*.{ts,tsx,js,jsx}: Remove unnecessary Effects; if the Effect only derives state, compute during render instead
Use useEffectEvent when listening to external events but needing the latest props/state without re-running the Effect
Move data fetching from client Effects to Server Components; mutations go through Server Actions ('use server')

Files:

  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterAutomaticAirdrops.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 add readonly before props in React components

**/*.{tsx,jsx}: Use internal links via <Link> component from Next.js instead of <a> tags
Replace <img> elements with <Image /> from next/image

Files:

  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterAutomaticAirdrops.tsx
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

Run npm run lint to ensure code satisfies ESLint (Next's Core Web Vitals + React Hooks). Code must pass linting before completing any task.

**/*.{js,ts,jsx,tsx}: Code must satisfy ESLint with Next's Core Web Vitals and React Hooks rules by running npm run lint
Do not add eslint-disable comments unless explicitly instructed; prefer refactors aligned with React 19.2, React Compiler, and Next.js 16 conventions

Files:

  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterAutomaticAirdrops.tsx
**/*.{jsx,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

**/*.{jsx,tsx}: Replace <img> elements with <Image /> from next/image to comply with Next.js ESLint rule @next/next/no-img-element.
Use <Link href="/path"> from Next.js for internal navigation instead of raw <a> tags to comply with @next/next/no-html-link-for-pages.

Files:

  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterAutomaticAirdrops.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

Use TypeScript with React functional components and hooks. Follow existing code style and naming conventions.

Files:

  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterAutomaticAirdrops.tsx
**/*.{tsx,ts}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript with React functional components and hooks

Files:

  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterAutomaticAirdrops.tsx
**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (AGENTS.md)

Prefer direct named imports from React (useMemo, useRef, FC) over React. namespace usage

Files:

  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterAutomaticAirdrops.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 (3)
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooterAutomaticAirdrops.tsx (3)

49-55: LGTM! Header detection logic is sound.

The isHeaderRow function correctly handles flexible header formats by accepting both "address"/"wallet" and "count"/"value" variations with case-insensitive matching.


135-137: LGTM! Correctly adapted to new parseCsv return type.

The destructuring of { rows } properly handles the updated signature.


170-173: CSV reconstruction maintains "count" terminology.

The reconstruction correctly normalizes the CSV data from parsed rows. However, note that line 171 uses ${row.count}, maintaining the "count" field name. This is consistent with the internal data structure but inconsistent with the UI hint showing "address,value" at line 235.

This reconstruction approach is appropriate for ensuring clean upload format.

Signed-off-by: prxt6529 <prxt@6529.io>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
__tests__/components/distribution/Distribution.test.tsx (1)

27-27: Consider removing explanatory comments per coding guidelines.

The coding guidelines specify that code should be self-explanatory without comments for TypeScript files. While these comments add clarity to the test setup, consider whether the code structure and naming alone can convey the same information.

That said, comments in test files are often valuable for explaining complex setup or test intent, so this is a lower-priority style suggestion.

As per coding guidelines for **/*.{ts,tsx,js,jsx}: "Do not include any comments in the code; it should be self-explanatory"

Also applies to: 33-33, 42-42, 50-50, 158-158

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7bf8803 and 2a0a101.

📒 Files selected for processing (1)
  • __tests__/components/distribution/Distribution.test.tsx
🧰 Additional context used
📓 Path-based instructions (10)
**/*.{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}: Remove unnecessary Effects. If the Effect's only job is to derive or sync internal state, calculate during render or use useMemo instead.
Use useEffectEvent for non-reactive logic inside Effects to read the latest props/state without turning them into dependencies or causing unnecessary re-runs.
Use explicit caching with "use cache" directive at the top of Server Components, routes, or functions. Configure cacheComponents: true in next.config.ts as needed.

**/*.{ts,tsx,js,jsx}: Remove unnecessary Effects; if the Effect only derives state, compute during render instead
Use useEffectEvent when listening to external events but needing the latest props/state without re-running the Effect
Move data fetching from client Effects to Server Components; mutations go through Server Actions ('use server')

Files:

  • __tests__/components/distribution/Distribution.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 add readonly before props in React components

**/*.{tsx,jsx}: Use internal links via <Link> component from Next.js instead of <a> tags
Replace <img> elements with <Image /> from next/image

Files:

  • __tests__/components/distribution/Distribution.test.tsx
**/*.{test,spec}.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

Run npm run test to execute all Jest tests and enforce ≥80% line coverage for files changed since main. Tests must pass and coverage threshold must be met before completing any task.

Enforce ≥ 80% line coverage for files changed since main by running npm run test

Files:

  • __tests__/components/distribution/Distribution.test.tsx
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

Run npm run lint to ensure code satisfies ESLint (Next's Core Web Vitals + React Hooks). Code must pass linting before completing any task.

**/*.{js,ts,jsx,tsx}: Code must satisfy ESLint with Next's Core Web Vitals and React Hooks rules by running npm run lint
Do not add eslint-disable comments unless explicitly instructed; prefer refactors aligned with React 19.2, React Compiler, and Next.js 16 conventions

Files:

  • __tests__/components/distribution/Distribution.test.tsx
**/*.{jsx,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

**/*.{jsx,tsx}: Replace <img> elements with <Image /> from next/image to comply with Next.js ESLint rule @next/next/no-img-element.
Use <Link href="/path"> from Next.js for internal navigation instead of raw <a> tags to comply with @next/next/no-html-link-for-pages.

Files:

  • __tests__/components/distribution/Distribution.test.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

Use TypeScript with React functional components and hooks. Follow existing code style and naming conventions.

Files:

  • __tests__/components/distribution/Distribution.test.tsx
**/*.{tsx,ts}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript with React functional components and hooks

Files:

  • __tests__/components/distribution/Distribution.test.tsx
**/{__tests__,*.test.{tsx,ts}}

📄 CodeRabbit inference engine (AGENTS.md)

Place tests in __tests__/ directory or as ComponentName.test.tsx alongside the component

Files:

  • __tests__/components/distribution/Distribution.test.tsx
**/*.{test,spec}.{tsx,ts}

📄 CodeRabbit inference engine (AGENTS.md)

Mock external dependencies and APIs in tests

Files:

  • __tests__/components/distribution/Distribution.test.tsx
**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (AGENTS.md)

Prefer direct named imports from React (useMemo, useRef, FC) over React. namespace usage

Files:

  • __tests__/components/distribution/Distribution.test.tsx
🧠 Learnings (7)
📚 Learning: 2025-12-30T14:32:19.339Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-30T14:32:19.339Z
Learning: Applies to **/*.{test,spec}.{tsx,ts} : Mock external dependencies and APIs in tests

Applied to files:

  • __tests__/components/distribution/Distribution.test.tsx
📚 Learning: 2025-12-30T14:31:52.993Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-30T14:31:52.993Z
Learning: Applies to **/__tests__/**/*.{test,spec}.{ts,tsx,js,jsx}|**/*.{test,spec}.{ts,tsx,js,jsx} : Place tests in `__tests__/` directory or as `ComponentName.test.tsx` files. Mock external dependencies and APIs in tests.

Applied to files:

  • __tests__/components/distribution/Distribution.test.tsx
📚 Learning: 2025-12-30T14:32:37.293Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-30T14:32:37.293Z
Learning: Applies to __tests__/**/__mocks__/**/*.{ts,tsx,js} : Jest automatically picks up mocks from `__mocks__` directory

Applied to files:

  • __tests__/components/distribution/Distribution.test.tsx
📚 Learning: 2025-12-30T14:32:37.293Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-30T14:32:37.293Z
Learning: Applies to __tests__/**/__tests__/**/*.{ts,tsx,js} : Use realistic production-like data in tests

Applied to files:

  • __tests__/components/distribution/Distribution.test.tsx
📚 Learning: 2025-12-30T14:32:37.293Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-30T14:32:37.293Z
Learning: Applies to __tests__/**/__tests__/**/*.{ts,tsx,js} : Write one behaviour per test with clear, descriptive test names

Applied to files:

  • __tests__/components/distribution/Distribution.test.tsx
📚 Learning: 2025-12-30T14:32:37.293Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-30T14:32:37.293Z
Learning: Applies to __tests__/**/*.{tsx,jsx},!**/__tests__/**,!**/__mocks__/** : Use semantic HTML (`<label>`, `<output>`) over ARIA attributes when possible

Applied to files:

  • __tests__/components/distribution/Distribution.test.tsx
📚 Learning: 2025-12-30T14:32:19.339Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-30T14:32:19.339Z
Learning: Applies to **/{__tests__,*.test.{tsx,ts}} : Place tests in `__tests__/` directory or as `ComponentName.test.tsx` alongside the component

Applied to files:

  • __tests__/components/distribution/Distribution.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)
🔇 Additional comments (1)
__tests__/components/distribution/Distribution.test.tsx (1)

185-772: Excellent test organization and coverage.

The test suite demonstrates strong testing practices:

  • Well-organized into logical describe blocks
  • Each test focuses on a single behavior
  • Proper async handling with waitFor
  • Comprehensive coverage of component functionality including edge cases
  • Effective use of mocks and test data
  • Proper cleanup with beforeEach

The overall structure and approach align well with testing best practices and the project's testing guidelines.

Based on learnings: Tests follow the principle of one behavior per test with clear, descriptive names and use realistic production-like data.

Comment thread __tests__/components/distribution/Distribution.test.tsx
Signed-off-by: prxt6529 <prxt@6529.io>
Signed-off-by: prxt6529 <prxt@6529.io>
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Jan 5, 2026

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (4)
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx (1)

185-202: Consider adding confirmation for destructive actions.

The "Reset Subscriptions" button now triggers the action directly without user confirmation. Since this is a destructive operation that could result in data loss, consider adding a simple confirmation dialog (e.g., window.confirm() or a lightweight modal) to prevent accidental resets.

🔎 Minimal confirmation example
          <button
            onClick={() =>
-              distributionPlan &&
-              resetSubscriptions(contract, confirmedTokenId, distributionPlan.id)
+              distributionPlan &&
+              window.confirm("Are you sure you want to reset subscriptions? This action cannot be undone.") &&
+              resetSubscriptions(contract, confirmedTokenId, distributionPlan.id)
            }
            disabled={isResetting}
openapi.yaml (3)

329-518: Distribution photo upload endpoints look consistent; consider adding 403 for Subscription Admin authorization failures

The new /distribution_photos/{contract}/{nft_id}/... endpoints nicely mirror the existing drop-media / wave-media multipart flows and reuse shared request/response schemas, which is good for consistency.

One detail: the descriptions say these require a Subscription Admin, but the responses only document 401 (Unauthorized) and not 403 (Forbidden). Other Subscription Admin–only APIs (e.g. /distributions/{contract}/{id}/overview) use 403 for role failures. It would be clearer for clients if the photo upload endpoints also declared a 403 response for “authenticated but not Subscription Admin” cases.

Consider adding a 403 response (e.g. "Forbidden - not a Subscription Admin") to all five distribution_photos upload endpoints, or otherwise aligning their documented auth error codes with your existing Subscription Admin endpoints.


9118-9139: Clarify automatic_airdrops_addresses naming to reflect it is a count

In DistributionOverview, the field:

  • automatic_airdrops_addresses is typed as an integer and described as “Count of unique addresses with automatic airdrops”.

The name reads like it might be an array of addresses, but it is actually a count. If it’s still easy to change, consider renaming to something like automatic_airdrops_addresses_count (or similar) to better match the type and description and avoid confusion for API consumers.

If renaming would be too disruptive, an alternative is to emphasize in the description that this is a numeric count, not a list.


9186-9216: Photo completion request/response models look good; consider minor tightening of constraints

The DistributionPhotoCompleteRequest* and DistributionPhotoCompleteResponse schemas correctly model:

  • A non‑empty list of photos to save (photos with minItems: 1) using media_url strings.
  • A response with success and the resulting list of photo URLs.

Two minor, optional refinements you might consider:

  • Add format: uri (and possibly a maxLength) to media_url and the response photos items, to align with how other URL fields are constrained (e.g. ApiDropMedia.url).
  • Optionally add minItems: 1 to DistributionPhotoCompleteResponse.photos if you expect that successful completion always returns at least one saved photo.

These aren’t required for correctness but would make the contract a bit more precise.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1a0efda and 9f57f15.

⛔ Files ignored due to path filters (2)
  • generated/models/DistributionOverview.ts is excluded by !**/generated/**
  • generated/models/ObjectSerializer.ts is excluded by !**/generated/**
📒 Files selected for processing (2)
  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx
  • openapi.yaml
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{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}: Remove unnecessary Effects. If the Effect's only job is to derive or sync internal state, calculate during render or use useMemo instead.
Use useEffectEvent for non-reactive logic inside Effects to read the latest props/state without turning them into dependencies or causing unnecessary re-runs.
Use explicit caching with "use cache" directive at the top of Server Components, routes, or functions. Configure cacheComponents: true in next.config.ts as needed.

**/*.{ts,tsx,js,jsx}: Remove unnecessary Effects; if the Effect only derives state, compute during render instead
Use useEffectEvent when listening to external events but needing the latest props/state without re-running the Effect
Move data fetching from client Effects to Server Components; mutations go through Server Actions ('use server')

Files:

  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.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 add readonly before props in React components

**/*.{tsx,jsx}: Use internal links via <Link> component from Next.js instead of <a> tags
Replace <img> elements with <Image /> from next/image

Files:

  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

Run npm run lint to ensure code satisfies ESLint (Next's Core Web Vitals + React Hooks). Code must pass linting before completing any task.

**/*.{js,ts,jsx,tsx}: Code must satisfy ESLint with Next's Core Web Vitals and React Hooks rules by running npm run lint
Do not add eslint-disable comments unless explicitly instructed; prefer refactors aligned with React 19.2, React Compiler, and Next.js 16 conventions

Files:

  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx
**/*.{jsx,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

**/*.{jsx,tsx}: Replace <img> elements with <Image /> from next/image to comply with Next.js ESLint rule @next/next/no-img-element.
Use <Link href="/path"> from Next.js for internal navigation instead of raw <a> tags to comply with @next/next/no-html-link-for-pages.

Files:

  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

Use TypeScript with React functional components and hooks. Follow existing code style and naming conventions.

Files:

  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx
**/*.{tsx,ts}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript with React functional components and hooks

Files:

  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx
**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (AGENTS.md)

Prefer direct named imports from React (useMemo, useRef, FC) over React. namespace usage

Files:

  • components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx
🧬 Code graph analysis (1)
components/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx (1)
services/distribution/distributionPhotoUpload.ts (1)
  • uploadDistributionPhotos (85-133)
⏰ 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/distribution-plan-tool/review-distribution-plan/table/ReviewDistributionPlanTableSubscriptionFooter.tsx (9)

1-16: LGTM!

Imports are clean and follow the coding guidelines with named imports from React. The refactored service import for uploadDistributionPhotos properly centralizes the upload logic.


17-25: LGTM!

Clean error normalization helper with proper type narrowing. Defined outside the component to avoid recreation on each render.


42-61: LGTM!

The refreshOverview callback is correctly memoized with an empty dependency array since React guarantees setState functions are stable across renders.


63-75: LGTM!

The effect correctly synchronizes with external state changes and has complete dependencies. The early returns provide clear guard clauses.


86-111: LGTM!

Well-structured async function with proper loading state management and error handling using the getErrorMessage helper.


113-148: LGTM!

The function properly handles both API-level failures (via response.success) and network/unexpected errors (via catch block). Loading state is correctly managed in the finally block.


259-284: LGTM!

The finalize button properly manages loading state and displays the current normalization status. Since finalization is typically an idempotent operation, the direct action without confirmation is reasonable here.


299-326: LGTM!

Clean integration with the uploadDistributionPhotos service. The handler properly manages UI state, provides user feedback via toast, and uses consistent error handling.


333-375: LGTM!

Consistent error handling pattern with the other handlers. The API response structure is properly typed and both success and failure paths are handled appropriately.

openapi.yaml (2)

577-608: Distribution overview endpoint is well-shaped and consistent

The new GET /distributions/{contract}/{id}/overview endpoint is straightforward, uses clear parameter descriptions, and returns the dedicated DistributionOverview schema. The documented 400 and 403 outcomes match the “Subscription Admin” requirement and are consistent with similar endpoints. No issues from the spec side here.


6036-6038: Check that ApiDropMetadata.data_value maxLength=5000 matches backend and UI constraints

Raising data_value’s maxLength to 5000 broadens what clients are allowed to send according to the spec. That’s fine as long as the underlying storage/validation (DB column length, server-side validators, and any front-end limits) are aligned; otherwise the OpenAPI may advertise values the backend will silently truncate or reject.

Please double‑check that your persistence and validation layers accept up to 5000 characters for this field, or adjust the documented maxLength accordingly.

@prxt6529 prxt6529 merged commit 9247c8f into main Jan 5, 2026
9 checks passed
simo6529 pushed a commit that referenced this pull request Jan 6, 2026
Signed-off-by: prxt6529 <prxt@6529.io>
@coderabbitai coderabbitai Bot mentioned this pull request Apr 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants