UploadInputV3 progress bar does not update during large chunked uploads

GOAL

Current state: In UploadInputV3, the progress bar for chunked uploads of large files (600MB or more) does not update incrementally during the upload. It shows an early partial value shortly after the transfer starts, then freezes for the remainder of the upload, and only jumps to "Complete" once the transfer finishes. This was confirmed via a screen recording showing the bar pixel-identical for 30+ seconds while chunk upload requests were still actively hitting the file-upload-api backend.

Target state: The progress bar advances smoothly and monotonically as chunks complete, regardless of file size, matching the behavior already present in the legacy (V1/V2) DOM-based progress bar.

This matters because sponsor users uploading large deliverables (500MB+ video or PDF assets) get no reliable feedback that the upload is progressing. A frozen bar for 30+ seconds looks like a hang and erodes trust in the upload feature, and increases support requests asking whether the upload is stuck.

TASKS

- Apply the existing _completedBytes anti-stall floor (already used by the legacy DOM progress bar in dropzone/index.js) to the React-facing V3 progress path in dropzone-v3.js, so reported progress can only move forward as chunks complete, never stall or regress between chunk boundaries
- Cap reported progress at 100 percent and guard against divide-by-zero for zero-size files in that same code path
- Stop DropzoneJS componentDidUpdate from unconditionally deep-cloning the full dropzone options object on every re-render, since a large chunked upload re-renders the component on every progress tick
- Add unit tests for the DropzoneV3 uploadprogress bridging covering the floor behavior, the 100 percent cap, and the zero-size file case

ACCEPTANCE CRITERIA

- Uploading a large file (500MB or more) via UploadInputV3 shows the progress bar advancing incrementally throughout the transfer, not just jumping at the very end
- New test suite src/components/inputs/upload-input-v3/__tests__/dropzone-v3.test.js passes: 3 tests covering the completed-bytes floor, the 100 percent cap, and the zero-size file case
- Full existing jest suite passes with no regressions
- No change to already-uploaded file list rendering, error handling, or async (HTTP 202) polling behavior

DEVELOPMENT NOTES

Key files:
src/components/inputs/upload-input-v3/dropzone-v3.js - React bridge between DropzoneJS events and UploadInputV3 callbacks, computes the progress percentage passed to the progress bar
src/components/inputs/dropzone/index.js - base DropzoneJS component, owns chunked upload plumbing, xhr handling, and the _completedBytes tracking this fix reuses
src/components/inputs/upload-input-v3/__tests__/dropzone-v3.test.js - new test file covering the fix

Gotchas:
_completedBytes is incremented by chunkSize (default 2000000 bytes) on every chunk's xhr.onload in dropzone/index.js. dropzone-v3.js now reads that same field as a floor instead of duplicating the tracking logic, so the two progress bars (legacy DOM and V3 React) stay consistent with a single source of truth.
Root cause was confirmed via a Jam.dev screen recording of a real 593MB upload in the sponsor portal, showing the progress bar frozen for 30+ seconds mid-transfer while chunk requests were still completing in the browser's network tab.

Risks:
Risk: componentDidUpdate now skips the options re-merge when djsConfig/postUrl are referentially unchanged, which could mask a case where a consumer mutates djsConfig in place instead of passing a new object.
Mitigation: djsConfig is already produced via useMemo in UploadInputV3 with stable dependencies, so referential equality holds for the intended usage pattern. Consumers that need the merge to re-run on every render were not relying on that behavior for anything observable.

Out of scope:
No changes to chunk size, concurrency defaults, or the async HTTP 202 polling flow.
No changes to the legacy V1/V2 DOM-based progress bar, which already had the _completedBytes floor.
