Skip to content

fix: comprehensive invoice validation and UI enhancements#178

Merged
kumawatkaran523 merged 7 commits into
StabilityNexus:mainfrom
Atharva0506:fix/invoice-validation
Jul 2, 2026
Merged

fix: comprehensive invoice validation and UI enhancements#178
kumawatkaran523 merged 7 commits into
StabilityNexus:mainfrom
Atharva0506:fix/invoice-validation

Conversation

@Atharva0506

@Atharva0506 Atharva0506 commented Jul 1, 2026

Copy link
Copy Markdown
Member

Addressed Issues:

Fixes #148

Screenshots/Recordings:

freecompress-first.mp4

Summary of Changes:

  • Amount Validation: Added robust checks to prevent negative values in Quantity, Unit Price, Discount, and Tax fields across both Single and Batch invoice pages.
  • Percentage Bounds: Constrained percentage-based discounts and taxes to strictly accept values between 0 and 100.
  • Dynamic Error Clearing: Updated state handlers (handleItemData, handleFieldChange) in CreateInvoice.jsx and CreateInvoicesBatch.jsx so that form validation errors immediately clear the moment a user types valid input, eliminating confusing lingering errors.
  • Batch Client Info Bug: Implemented the missing handleInvoiceChange function in CreateInvoicesBatch.jsx, fixing a bug that completely broke input handling for Client Information fields in batch mode.
  • UI Consistency: Added the AlertCircle icon to batch line item error states to perfectly match the design language of the single invoice page.

Additional Notes:

AI Usage Disclosure:

We encourage contributors to use AI tools responsibly when creating Pull Requests. While AI can be a valuable aid, it is essential to ensure that your contributions meet the task requirements, build successfully, include relevant tests, and pass all linters. Submissions that do not meet these standards may be closed without warning to maintain the quality and integrity of the project. Please take the time to understand the changes you are proposing and their impact. AI slop is strongly discouraged and may lead to banning and blocking. Do not spam our repos with AI slop.

Check one of the checkboxes below:

  • This PR does not contain AI-generated code at all.
  • This PR contains AI-generated code. I have read the AI Usage Policy and this PR complies with this policy. I have tested the code locally and I am responsible for it.

I have used the following AI models and tools: model-Gemini 3.1High, Used for testing

Checklist

  • My PR addresses a single issue, fixes a single bug or makes a single improvement.
  • My code follows the project's code style and conventions
  • If applicable, I have made corresponding changes or additions to the documentation
  • If applicable, I have made corresponding changes or additions to tests
  • My changes generate no new warnings or errors
  • I have joined the Discord server and I will share a link to this PR with the project maintainers there
  • I have read the Contribution Guidelines
  • Once I submit my PR, CodeRabbit AI will automatically review it and I will address CodeRabbit's comments.
  • I have filled this PR template completely and carefully, and I understand that my PR may be closed without review otherwise.

Summary by CodeRabbit

  • New Features
    • Added an inline toggle to switch invoice discount/tax between flat values and percentages.
  • Bug Fixes
    • Improved invoice creation and batch invoice flows with centralized amount calculations, safer decimals handling, stronger validation, and clearer inline errors.
    • Enhanced token decimals resolution to reduce payment total precision issues.
    • Improved token picking UX by disabling competing interactions while a token selection is in progress.
    • Updated the copy action to be more accessible and prevent unintended default browser behavior.

@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@Atharva0506, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 9 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 853a09cc-a8e2-40d5-9852-771469f6f83b

📥 Commits

Reviewing files that changed from the base of the PR and between 997b8d9 and 5406cd0.

📒 Files selected for processing (2)
  • frontend/src/page/CreateInvoicesBatch.jsx
  • frontend/src/utils/invoiceValidation.js

Walkthrough

This PR adds shared invoice amount and validation helpers, wires them into single and batch invoice creation flows, introduces amount-type toggles, resolves token decimals dynamically, hardens token selection interactions, and updates the copy action to work from the keyboard.

Changes

Invoice calculation and validation rework

Layer / File(s) Summary
Shared amount helpers
frontend/src/utils/invoiceCalculations.js, frontend/src/components/AmountTypeToggle.jsx, frontend/src/utils/productCatalogInvoiceHelpers.js
Adds shared line-amount parsing/calculation/formatting helpers, a toggle for amount versus percentage, and product-item defaults that use the shared display helper.
Invoice validation rules
frontend/src/utils/invoiceValidation.js
Adds shared client-address, line-item, single-invoice, and batch-invoice validation with structured error maps and token-decimal checks.
CreateInvoice integration
frontend/src/page/CreateInvoice.jsx
Uses the shared helpers for totals, validation, token-decimal resolution, payload normalization, and inline field/item error rendering.
CreateInvoicesBatch integration
frontend/src/page/CreateInvoicesBatch.jsx
Uses the shared helpers for batch totals, validation, token-decimal resolution, row error tracking, payload shaping, and inline row/item error rendering.

Estimated code review effort: 4 (Complex) | ~75 minutes

TokenPicker and CopyButton UX fixes

Layer / File(s) Summary
Async token selection guards
frontend/src/components/TokenPicker.jsx
Adds in-flight selection state, disables overlapping interactions, and shows loading feedback for the token being selected.
CopyButton keyboard handling
frontend/src/components/ui/copyButton.jsx
Prevents default click behavior and supports keyboard activation via Enter and Space.

Estimated code review effort: 2 (Simple) | ~15 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant CreateInvoice
  participant invoiceValidation
  participant resolveTokenDecimals
  participant Contract

  User->>CreateInvoice: edit item / submit invoice
  CreateInvoice->>invoiceValidation: validateSingleInvoiceData(...)
  invoiceValidation-->>CreateInvoice: fieldErrors, isValid
  CreateInvoice->>resolveTokenDecimals: fetch token decimals
  resolveTokenDecimals-->>CreateInvoice: decimals or null
  CreateInvoice->>Contract: createInvoice(normalizedData, amountDue)
Loading

Possibly related PRs

Suggested labels: Typescript Lang

Suggested reviewers: adityabhattad2021

Poem

I hopped through totals, neat and bright,
No negative lines escaped tonight.
Flat or percent, the toggle sings,
Token picks now wait on their things.
A bunny clicks with keys in tune,
And invoices behave by moon. 🐇

🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning copyButton.jsx adds unrelated button-semantic changes that are outside the invoice validation scope. Move the copy button accessibility refactor to a separate PR, or explain how it directly supports the invoice fix context.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is clear and matches the PR's main invoice validation and UI focus.
Linked Issues check ✅ Passed The PR addresses #148 by blocking negative line amounts, validating inputs, and preventing invalid invoice submission.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@Atharva0506 Atharva0506 marked this pull request as ready for review July 1, 2026 16:55

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 9

Caution

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

⚠️ Outside diff range comments (1)
frontend/src/components/ui/copyButton.jsx (1)

20-46: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Prefer a native <button type="button"> over span+role reimplementation.

The manual role="button" + tabIndex={0} + onKeyDown pattern re-implements keyboard activation and focus semantics that a native <button> already provides for free. If the original motivation was to avoid accidental form submission (since CopyButton is used inside <form onSubmit=...> in CreateInvoice.jsx/CreateInvoicesBatch.jsx), adding type="button" to a real <button> element solves that without the extra custom keydown handling.

♻️ Simpler alternative using a native button
   return (
-    <span
-      role="button"
-      tabIndex={0}
+    <button
+      type="button"
       onClick={handleCopy}
-      onKeyDown={handleKeyDown}
       className={`inline-flex cursor-pointer items-center gap-1 px-2 py-1 text-xs rounded hover:bg-gray-100 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-gray-400 ${className}`}
       title="Copy address"
     >
       {copied ? (
         <>
           <Check className="w-3 h-3 text-green-600" />
           <span className="text-green-600">Copied!</span>
         </>
       ) : (
         <>
           <Copy className="w-3 h-3 text-gray-500" />
           <span className="text-gray-500">Copy</span>
         </>
       )}
-    </span>
+    </button>
   );

(handleKeyDown can then be removed entirely.)

As per path instructions, "The code adheres to best practices associated with React."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/components/ui/copyButton.jsx` around lines 20 - 46, The
CopyButton component is reimplementing button behavior with a span, role,
tabIndex, and custom key handling. Update CopyButton to use a native button
element with type="button" so it works correctly inside forms used by
CreateInvoice and CreateInvoicesBatch, and remove the manual handleKeyDown logic
since the browser will handle keyboard activation and focus semantics.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend/src/components/AmountTypeToggle.jsx`:
- Around line 6-31: The AmountTypeToggle component exposes the current “Flat” /
“%” state only through styling, so update its buttons to communicate state to
assistive tech. Add aria-pressed to the two state buttons in AmountTypeToggle
and give the icon-only toggle button an explicit accessible label via aria-label
(while keeping the existing onClick behavior and ArrowRightLeft control). Use
the component’s value/onChange logic to reflect the active state consistently
for screen readers.

In `@frontend/src/components/TokenPicker.jsx`:
- Around line 214-235: The open modal still allows interaction when disabled
state changes or the chain switches to a testnet because handlers like
handleSelect and handleCustomTokenClick only guard on isSelecting. Centralize an
interaction-disabled check in TokenPicker (using the same disabled/testnet
gating as the trigger) and apply it to handleSelect, token row clicks, the
query-clear button, and the custom-token action so the modal cannot bypass the
parent gating.

In `@frontend/src/page/CreateInvoice.jsx`:
- Around line 1279-1289: The AmountTypeToggle handlers in CreateInvoice are
updating every entry in setItemData instead of only the edited line item, which
can change unrelated rows’ discount/tax behavior and totals. Update the onChange
logic for the per-row toggles (both the discount and tax controls) so it targets
only the current item by index or a stable item identifier, and recomputes
amount for that single line using getSafeLineAmountDisplay while leaving other
items unchanged.
- Around line 407-413: The call to validateSingleInvoiceData in CreateInvoice is
missing required identity fields, causing required-field validation failures.
Update the validation payload to pass userFname, userEmail, clientFname, and
clientEmail from the current form data alongside clientAddress, itemData,
totalAmountDue, paymentToken, and ownerAddress, and verify the CreateInvoice
submit flow uses the same field names expected by validateSingleInvoiceData.

In `@frontend/src/page/CreateInvoicesBatch.jsx`:
- Around line 467-480: The batch invoice flow is still relying on a hardcoded
18-decimal token selection path, which can mis-scale amounts for non-18 tokens.
Update the token-picking and submission flow in CreateInvoicesBatch.jsx so
selectedToken preserves the validated decimals from the token list/provider, or
reuse the same decimals resolution logic used by the single-invoice path before
validateInvoicesBeforeSubmit runs. Make sure the token picker does not overwrite
decimals with 18 and that the invoice amount parsing uses the resolved token
decimals consistently in both affected sections.
- Around line 1288-1308: The per-item mobile toggle handler in
CreateInvoicesBatch is updating every entry in rowIndex’s itemData array instead
of only the edited item, so changing one line’s discount/tax type mutates
siblings and recalculates all amounts. Update the onChange logic for
AmountTypeToggle to target only the specific item being edited (using the item’s
own index or identifier) while leaving the rest of r.itemData unchanged, and
apply the same fix to the matching tax-type toggle block referenced in the other
section.
- Around line 169-177: The row-removal logic in CreateInvoicesBatch only
reindexes clientAddressErrors, so rowTotalErrors, rowItemErrors, and row-scoped
fieldErrors can remain tied to the deleted index and appear on the wrong
invoice. Update removeInvoiceRow to reindex every row-based error map using a
shared helper like reindexRowErrors, and apply it consistently to all affected
state setters so all remaining rows shift their errors down together.

In `@frontend/src/utils/invoiceValidation.js`:
- Around line 132-138: The line-item validation loop in invoiceValidation should
skip auto-appended untouched blank rows instead of passing them into
getLineItemError, since those empty items are triggering required-field errors
and blocking submission. Update the validation logic around the itemData
iteration so only populated line items are checked, while preserving existing
behavior for real item rows and keeping itemErrorsObj/hasItemErrors in sync.
- Around line 58-70: The submit-time validation in invoiceValidation.js only
checks for negative discountWei/taxRateWei and misses percentage upper bounds
for imported or manipulated state. Update the validator logic around the
existing discount/tax checks to also reject discountType or taxType values set
to "percentage" when their computed wei values exceed 100%, and add
corresponding errors so the submit path matches the direct-input limits. Use the
existing validation flow in the invoice validation function and the
errors.discount/errors.tax assignments to locate and extend the checks.

---

Outside diff comments:
In `@frontend/src/components/ui/copyButton.jsx`:
- Around line 20-46: The CopyButton component is reimplementing button behavior
with a span, role, tabIndex, and custom key handling. Update CopyButton to use a
native button element with type="button" so it works correctly inside forms used
by CreateInvoice and CreateInvoicesBatch, and remove the manual handleKeyDown
logic since the browser will handle keyboard activation and focus semantics.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: c67c0664-dbed-404a-9dc4-d945565369bc

📥 Commits

Reviewing files that changed from the base of the PR and between 2b03fa4 and 9ed6593.

📒 Files selected for processing (8)
  • frontend/src/components/AmountTypeToggle.jsx
  • frontend/src/components/TokenPicker.jsx
  • frontend/src/components/ui/copyButton.jsx
  • frontend/src/page/CreateInvoice.jsx
  • frontend/src/page/CreateInvoicesBatch.jsx
  • frontend/src/utils/invoiceCalculations.js
  • frontend/src/utils/invoiceValidation.js
  • frontend/src/utils/productCatalogInvoiceHelpers.js

Comment thread frontend/src/components/AmountTypeToggle.jsx
Comment thread frontend/src/components/TokenPicker.jsx Outdated
Comment thread frontend/src/page/CreateInvoice.jsx
Comment thread frontend/src/page/CreateInvoice.jsx
Comment thread frontend/src/page/CreateInvoicesBatch.jsx Outdated
Comment thread frontend/src/page/CreateInvoicesBatch.jsx
Comment thread frontend/src/page/CreateInvoicesBatch.jsx
Comment thread frontend/src/utils/invoiceValidation.js
Comment thread frontend/src/utils/invoiceValidation.js
@Atharva0506 Atharva0506 self-assigned this Jul 2, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend/src/page/CreateInvoicesBatch.jsx`:
- Around line 211-235: `fieldErrors` is being reindexed with
`reindexSimpleErrors`, but that helper only works for plain numeric keys, so
row-scoped entries like `0_clientEmail` or nonnumeric field keys are lost when a
row is removed. Update the row-removal logic in `CreateInvoicesBatch` to use a
field-error-specific reindexer (near `reindexSimpleErrors`/`reindexItemErrors`)
that preserves non-row keys and shifts row-prefixed keys down by one, then pass
that helper to `setFieldErrors` instead of the simple numeric version.

In `@frontend/src/utils/invoiceValidation.js`:
- Around line 58-67: The percentage cap checks in validateInvoice values are
comparing the raw string via Number(item.discount || 0) and Number(item.tax ||
0), which can miss values slightly above 100 due to rounding. Update the
discount and tax validation branches in invoiceValidation.js to compare
discountWei and taxRateWei against a parsed 100 constant in wei form instead, so
the existing discountType and taxType percentage checks reject any amount over
the cap precisely.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: ecfc8c1f-598a-4f0d-acba-8b6b16815c18

📥 Commits

Reviewing files that changed from the base of the PR and between 9ed6593 and 997b8d9.

📒 Files selected for processing (5)
  • frontend/src/components/AmountTypeToggle.jsx
  • frontend/src/components/TokenPicker.jsx
  • frontend/src/page/CreateInvoice.jsx
  • frontend/src/page/CreateInvoicesBatch.jsx
  • frontend/src/utils/invoiceValidation.js

Comment thread frontend/src/page/CreateInvoicesBatch.jsx
Comment thread frontend/src/utils/invoiceValidation.js
@kumawatkaran523 kumawatkaran523 merged commit 584c0dc into StabilityNexus:main Jul 2, 2026
2 checks passed
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.

[BUG]: Negative invoice line amount shown and invalid invoice submission allowed

2 participants